一、前言
官网文档 https://sentinelguard.io/zh-cn/index.html
二、Sentinel安装
tips: 通过docker-compose 方式安装
git clone https://gitee.com/zhengqingya/docker-compose.git
cd docker-compose/Liunx
docker-compose -f docker-compose-sentinel.yml -p sentinel up -d
三、Sentinel使用说明
1、实时监控
秒级展示
2、簇点链路
仅展示服务启动后调用过的资源
3、流控规则
QPS:每秒查询率,一台服务器每秒能够响应的请求次数 并发线程数:访问该资源的并发线程数
流控模式
-
直接:直接操作, 对当前资源限流 -
关联:当关联资源达到阈值时限流自己 ( ex: 设置关联资源B, 当B达到阈值, 对A做限流 ) -
链路:只统计从指定资源进入当前资源的请求,是针对请求来源的限流 A -> common B -> common 仅限流通过入口B访问的common,A随意访问不受影响 ( tips: 如果B每次都能正常请求到common的话,效果和直接流控一样,如果未正常请求common则相当于未限流处理 )
tips: 链路测试需配置 spring.cloud.sentinel.web-context-unify: false 关闭调用链路收敛 => 实现链路流控
流控效果
- 快速失败:达到阈值后,新的请求就会被立即拒绝
- Warm Up:预热/冷启动方式,让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮
- 排队等待:面对突发的流量,排队匀速通过,例如消息队列,对应的是漏桶算法,选择排队只能使用QPS(不能用并发线程数),当到达超时间就失败
4、熔断规则
慢调用比例
当单位统计时长内,请求数超过3 & 最大RT(响应时间)超过10毫秒的有10%,则对请求进行熔断,熔断时长为3秒,3秒后的一个请求响应时间<RT则结束熔断恢复正常
异常降级仅针对业务异常,对 Sentinel 限流降级本身的异常(BlockException)不生效。为了统计异常比例或异常数,需要通过 Tracer.trace(ex) 记录业务异常。
异常比例
当单位统计时长内,请求数超过3 & 异常的比例大于阈值10%之后会自动进行熔断,若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。
异常数
当单位统计时长内,请求数超过3 & 异常数大于阈值10%之后会自动进行熔断,若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。
5、热点规则
a、方法上加上注解@SentinelResource("xxx")
b、控制台新增热点规则
仅对包含热点参数的资源调用生效
参数索引:0代表第一个参数,1代表第二个参数。 当单位统计窗口时长1s内,请求次数超过单机阈值1时生效
6、系统规则
Sentinel 系统自适应限流从整体维度对应用入口流量进行控制,结合应用的 Load、CPU 使用率、总体平均 RT、入口 QPS 和并发线程数等几个维度的监控指标,通过自适应的流控策略,让系统的入口流量和系统的负载达到一个平衡,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。
- Load 自适应(仅对 Linux/Unix-like 机器生效):系统的 load1 作为启发指标,进行自适应系统保护。当系统 load1 超过设定的启发值,且系统当前的并发线程数超过估算的系统容量时才会触发系统保护(BBR 阶段)。系统容量由系统的
maxQps * minRt 估算得出。设定参考值一般是 CPU cores * 2.5 。 - CPU usage(1.5.0+ 版本):当系统 CPU 使用率超过阈值即触发系统保护(取值范围 0.0-1.0),比较灵敏。
- 平均 RT:当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒。
- 并发线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。
- 入口 QPS:当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。
7、授权规则
a、实现RequestOriginParser 类获取流控应用值
@Component
public class SentinelRequestOriginParser implements RequestOriginParser {
@Override
public String parseOrigin(HttpServletRequest request) {
String origin = request.getParameter("origin");
if (StringUtils.isBlank(origin)) {
origin = " ";
}
return origin;
}
}
b、控制台新增授权规则
白名单内的服务可正常访问,黑名单内的拒绝访问
8、集群流控
部署方式:
-
独立模式 :Token Server 单独运行 -
嵌入模式 :Token Server 与服务在同一进程中启动,无需单独部署
Token Client :集群流控客户端,用于向所属 Token Server 通信请求 token。集群限流服务端会返回给客户端结果,决定是否限流。Sentinel 集群流控的通信底层采用 Netty 实现。Token Server :集群流控服务端,处理来自 Token Client 的请求,根据配置的集群规则判断是否应该发放 token(是否允许通过)。
集群流控后面有时间再研究吧^_^
9、机器列表
客户端实例监控
如果健康状态为失联 则需要自己选择性移除
四、sentinel 统一异常处理
@Component
public class MySentinelExceptionHandler implements BlockExceptionHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, BlockException ex) throws Exception {
String msg = null;
if (ex instanceof FlowException) {
msg = "访问频繁,请稍候再试";
} else if (ex instanceof DegradeException) {
msg = "系统降级";
} else if (ex instanceof ParamFlowException) {
msg = "热点参数限流";
} else if (ex instanceof SystemBlockException) {
msg = "系统规则限流或降级";
} else if (ex instanceof AuthorityException) {
msg = "授权规则不通过";
} else {
msg = "未知限流降级";
}
response.setStatus(500);
response.setCharacterEncoding("utf-8");
response.setHeader("Content-Type", "application/json;charset=utf-8");
response.setContentType("application/json;charset=utf-8");
response.getWriter().write(JSON.toJSONString(ApiResult.fail(msg)));
}
}
五、本文测试api代码
可查看 https://gitee.com/zhengqingya/small-tools
@Slf4j
@RestController
@RequestMapping("/sentinel")
@Api(tags = "sentinel")
public class SentinelController extends BaseController {
@Resource
private ISentinelService iSentinelService;
@Resource
private IDemoClient iDemoClient;
private Long num = 0L;
private Long requestCommon() {
return this.iDemoClient.flowCommon();
}
@SneakyThrows(Exception.class)
@GetMapping("flow/A")
@ApiOperation("A")
public Long flowA() {
this.num++;
if (this.num > 6) {
return this.num;
}
this.requestCommon();
return RandomUtil.randomLong();
}
@SneakyThrows(Exception.class)
@GetMapping("flow/B")
@ApiOperation("B")
public Long flowB() {
this.requestCommon();
return RandomUtil.randomLong();
}
@SneakyThrows(Exception.class)
@GetMapping("flow/C")
@ApiOperation("C")
public Long flowC() {
TimeUnit.MILLISECONDS.sleep(200);
return RandomUtil.randomLong();
}
@SneakyThrows(Exception.class)
@GetMapping("flow/D")
@ApiOperation("D")
public Long flowD() {
try {
int a = 1 / 0;
} catch (Exception e) {
if (!BlockException.isBlockException(e)) {
Tracer.trace(e);
}
throw e;
}
return RandomUtil.randomLong();
}
@SentinelResource("/sentinel/flow/EE")
@SneakyThrows(Exception.class)
@GetMapping("flow/E")
@ApiOperation("E")
public Long flowE(@RequestParam(required = false) String paramA,
@RequestParam(required = false) String paramB) {
return RandomUtil.randomLong();
}
@SneakyThrows(Exception.class)
@GetMapping("F")
@ApiOperation("F")
public Long F() {
return RandomUtil.randomLong();
}
}
今日分享语句: 其实所有的努力,都不是给别人看的。这些努力是否有意义,在于它是否丰厚了你的学识,加固了你的基础,转变成了你的能力。努力需要有成效,而不是看上去的忙碌辛苦。
|