Sentinel基本使用--基于QPS流量控制(一), 采用默认快速失败/直接拒绝策略控制超过阈值的流量(结合Dashboard使用)
随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 是面向分布式服务架构的轻量级流量控制框架,主要以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度来帮助您保护服务的稳定性。流量控制有以下几个角度:资源的调用关系,例如资源的调用链路,资源和资源之间的关系;运行指标,例如 QPS、线程池、系统负载等;控制的效果,例如直接限流、冷启动、排队等。Sent...
随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 是面向分布式服务架构的轻量级流量控制框架,主要以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度来帮助您保护服务的稳定性。
流量控制有以下几个角度:
- 资源的调用关系,例如资源的调用链路,资源和资源之间的关系;
- 运行指标,例如 QPS、线程池、系统负载等;
- 控制的效果,例如直接限流、冷启动、排队等。
Sentinel 的设计理念是让用户可以自由选择控制的角度,并进行灵活组合,从而达到想要的效果。
一, Sentinel 的使用可以分为两个部分:
- 核心库(Java 客户端):不依赖任何框架/库,能够运行于 Java 7 及以上的版本的运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。
- 控制台(Dashboard):Dashboard主要负责管理推送规则;监控;管理机器信息等。基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器。
二, 引入核心库和客户端与dashboard控制台通信的依赖
<!-- sentinel核心库依赖 -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
<version>1.4.0</version>
</dependency>
<!-- sentinel客户端与dashboard通信依赖 -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-transport-simple-http</artifactId>
<version>1.4.0</version>
</dependency>
三, 定义资源
资源 是 Sentinel 中的核心概念之一。最常用的资源是我们代码中的 Java 方法, 它可以是 Java 应用程序中的任何内容, 或是应用程序提供某一项服务的业务代码, 或是应用程序调用其他应用提供的服务接口, 或者直接就是一段代码, 由此可知sentinel中的资源可以是应用程序中具体的某一行代码, 流控的粒度非常细.
只要通过 Sentinel API 定义的代码,就是资源,能够被 Sentinel 保护起来。大部分情况下,可以使用方法签名,URL,甚至服务名称作为资源名来标示资源。
例如: 使用Sentinel Api将被访问的资源包围起来.
Entry entry = null;
// 务必保证finally会被执行
try {
// 资源名可使用任意有业务语义的字符串
entry = SphU.entry("自定义资源名");
// 被保护的业务逻辑, 即被保护的资源
// do something...
} catch (BlockException e1) {
// 资源访问阻止,被限流或被降级
// 进行相应的处理操作
} finally {
if (entry != null) {
entry.exit();
}
}
四, 定义访问该资源的规则
资源定义完成后, 就需要对该资源定义一些访问规则, 如针对上面的资源, 基于QPS的流行控制形式, 采用默认的RuleConstant.CONTROL_BEHAVIOR_DEFAULT;配置每秒最多允许通过20次请求.
private static void initFlowRules(){
List<FlowRule> rules = new ArrayList<>();
FlowRule rule = new FlowRule();
rule.setResource("HelloWorld");
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
// Set limit QPS to 20.
rule.setCount(20);
rules.add(rule);
FlowRuleManager.loadRules(rules);
}
五, 启动sentinel dashboard控制台
使用下面命令启动dashboard:
java -Dserver.port=8080 -jar sentinel-dashboard.jar
-Dserver.port=8080为sentinel控制台的启动端口, 如下就代表着启动成功.
访问控制台: 由于现在还没有任何客户端接入控制台, 所以看不到应用.
六, 客户端接入dashboard控制台
这里以sentinel自带的sentinel-demo-basic自带的demo为例, 运行FlowQpsDemo类, 并配置上控制台地址.
-Dcsp.sentinel.dashboard.server=127.0.0.1:8080 -- sentinel 控制台ip:port
-Dcsp.sentinel.api.port=8719 -- 客户端用于接收控制台流控规则端口号
-Dproject.name=FlowQpsDemo -- 名称
FlowQpsDemo类,
1 首先定义资源, 成功访问资源时pass+1, 阻止访问资源时会抛出BlockException则block+1.
2 接着定义访问资源的规则:
rule1.setResource(KEY);资源名,即限流规则的作用对象
rule1.setCount(20);设置允许通过的最大请求数20; 限流阈值
rule1.setGrade(RuleConstant.FLOW_GRADE_QPS);设置限流阈值类型, QPS 或线程数模式, 默认是QPS.
rule1.setLimitApp("default");流控针对的调用来源,若为 default
则不区分调用来源
这里没有在代码显示的配置流控效果, 而当流量超过阈值时, 默认采用的流控效果就是直接拒绝即快速失败, 或者我们也可以直接配置上:
rule1.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);
下面配上流控规则定义的一些重要属性:
Field | 说明 | 默认值 |
---|---|---|
resource | 资源名,资源名是限流规则的作用对象 | |
count | 限流阈值 | |
grade | 限流阈值类型,QPS 或线程数模式 | QPS 模式 |
limitApp | 流控针对的调用来源 | default ,代表不区分调用来源 |
strategy | 判断的根据是资源自身,还是根据其它关联资源 (refResource ),还是根据链路入口 | 根据资源本身 |
controlBehavior | 流控效果(直接拒绝 / 排队等待 / 慢启动模式) | 直接拒绝 |
同一个资源可以同时有多个限流规则。
public class FlowQpsDemo {
private static final String KEY = "abc";
// 统计通过数
private static AtomicInteger pass = new AtomicInteger();
// 统计阻止数
private static AtomicInteger block = new AtomicInteger();
// 统计总数
private static AtomicInteger total = new AtomicInteger();
private static volatile boolean stop = false;
private static final int threadCount = 32;
// 运行100s
private static int seconds = 60 + 40;
public static void main(String[] args) throws Exception {
// 初始化流控规则
initFlowQpsRule();
// 1s计时统计, pass, block, total
tick();
// 模拟开启32个线程并发访问资源
simulateTraffic();
System.out.println("===== begin to do flow control");
System.out.println("only 20 requests per second can pass");
}
private static void initFlowQpsRule() {
List<FlowRule> rules = new ArrayList<FlowRule>();
FlowRule rule1 = new FlowRule();
rule1.setResource(KEY);
// set limit qps to 20
rule1.setCount(20);
rule1.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule1.setLimitApp("default");
rules.add(rule1);
FlowRuleManager.loadRules(rules);
}
private static void simulateTraffic() {
for (int i = 0; i < threadCount; i++) {
Thread t = new Thread(new RunTask());
t.setName("simulate-traffic-Task");
t.start();
}
}
static class RunTask implements Runnable {
@Override
public void run() {
while (!stop) {
Entry entry = null;
try {
entry = SphU.entry(KEY);
// token acquired, means pass
pass.addAndGet(1);
} catch (BlockException e1) {
block.incrementAndGet();
} catch (Exception e2) {
// biz exception
} finally {
total.incrementAndGet();
if (entry != null) {
entry.exit();
}
}
Random random2 = new Random();
try {
TimeUnit.MILLISECONDS.sleep(random2.nextInt(50));
} catch (InterruptedException e) {
// ignore
}
}
}
}
private static void tick() {
Thread timer = new Thread(new TimerTask());
timer.setName("sentinel-timer-task");
timer.start();
}
static class TimerTask implements Runnable {
@Override
public void run() {
long start = System.currentTimeMillis();
System.out.println("begin to statistic!!!");
long oldTotal = 0;
long oldPass = 0;
long oldBlock = 0;
while (!stop) {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
}
long globalTotal = total.get();
long oneSecondTotal = globalTotal - oldTotal;
oldTotal = globalTotal;
long globalPass = pass.get();
long oneSecondPass = globalPass - oldPass;
oldPass = globalPass;
long globalBlock = block.get();
long oneSecondBlock = globalBlock - oldBlock;
oldBlock = globalBlock;
System.out.println(seconds + " send qps is: " + oneSecondTotal);
System.out.println(TimeUtil.currentTimeMillis() + ", total:" + oneSecondTotal + ", pass:"
+ oneSecondPass + ", block:" + oneSecondBlock);
if (seconds-- <= 0) {
stop = true;
}
}
long cost = System.currentTimeMillis() - start;
System.out.println("time cost: " + cost + " ms");
System.out.println("total:" + total.get() + ", pass:" + pass.get() + ", block:" + block.get());
System.exit(0);
}
}
}
后台运行效果, 以及控制台展示:
实时监控, 统计的p_qps=20.
簇点链路展示, 资源名称abc, 通过的QPS, 拒绝的QPS, 线程数, 平均RT, 分钟通过数, 分钟拒绝数
同时, 还可以通过dashboard控制台修改针对该资源的流控规则, 推送给客户端实时应用该规则, 如下图, 修改流控规则QPS为10
则后台console端展示的就是修改后, 最大允许通过QPS为10的情况.
对应的实时监控, 通过的p_qps统计也是10.
而流控规则, 此时对应展示的单机阈值10, 注意这里的流控效果: 快速失败(RuleConstant.CONTROL_BEHAVIOR_DEFAULT则
是默认处理超过阈值流量的策略), 指的是, 当QPS超过任意规则的阈值后, 新的请求会被直接拒绝, 拒绝方式为抛出FlowException, FlowException
是 BlockException
的子类, 如下图:
七, 总结
主要介绍了sentinel的基本使用, 定义资源, 定义访问该资源的规则, 如何接入dashboard监控台实时修改流控规则. 但是, 在这里也得注意一下, 监控台修改的规则推送给客户端, 修改客户端的流控规则都是基于内存的, 一旦客户端应用重启, 那么刚刚上面配置的流控规则就会失效, 这时的流控规则就是客户端启动时的最原始配置的流控规则, 所以将dashboard监控台应用于生产环境时, 还需要将流控规则持久化, 当然了sentinel也提供了开放的接口,可以通过实现 DataSource接口的方式,来自定义规则的存储数据源。整合动态配置系统,如 ZooKeeper、Nacos 等,动态地实时刷新配置规则; 结合 RDBMS、NoSQL、VCS 等来实现该规则.
详细请参考:
到这里, 我们结合sentinel的dashboard, 实现了基于QPS的流量控制, 采用默认快速失败/直接拒绝策略控制超过阈值的流量, 从而对资源进行保护.
更多推荐
所有评论(0)