本系列文章通过学习Sentinel官方文档而后整理
Sentinel是分布式系统的流量卫兵,Sentinel以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
首先需要理解Sentinel中的2个概念,资源 和 规则
资源 在Sentinel中是受保护的对象,资源可以是任何项目中的东西,可以是服务、方法(接口)、若干行代码
规则 是制定来规范如何保护资源的,Sentinel提供在任何时候都可以灵活地定义流量控制规则a
所以在官方文档中,定义的Sentinel进行资源保护的几个步骤:
- 定义资源
- 定义规则
- 检验规则是否生效
Sentinel适配大部分主流框架,Dubbo,Spring Cloud,gRPC等等,但是一切从简,让我们从最简单的maven环境体验Sentinel的本领
首先我们需要定义资源,至于如何定义资源,主要有以下2种方式
- Sentinel API,使用SphU.entry("自定义资源名")和entry.exit()包裹的代码,就是资源
try {
// 资源名可使用任意有业务语义的字符串
entry = SphU.entry("自定义资源名");
// 被保护的逻辑
} catch (BlockException e1) {
// 处理被流控的逻辑
} finally {
if (entry != null) {
entry.exit();
}
}
- 注解,通过@SentinelResource注解声明该方法为资源
@SentinelResource("自定义资源名")
public void helloWorld() {
// 被保护的逻辑
}
学习了俩种总的调用方法以后,让我们细化到代码上,来看看其他几种不通的代码实现方式:
- 不需要收到exit的try-with-resource
try (Entry entry = SphU.entry("resourceName")) {
// 被保护的逻辑
} catch (BlockException ex) {
// 处理被流控的逻辑
}
- 其实就是上面的API风格,不过这里拓展一个点,就是entry的额外参数,可以传入热点参数,不过注意,如果entry有额外参数,
需要在exit的时候也加上那些参数,否则可能会出现统计错误(热点参数问题以后再谈)
try {
// 资源名可使用任意有业务语义的字符串
entry = SphU.entry("自定义资源名");
// 被保护的逻辑
} catch (BlockException e1) {
// 处理被流控的逻辑
} finally {
if (entry != null) {
entry.exit();
}
}
3.可以使用SphO的布尔值来考虑限流逻辑,通过if-else
if (SphO.entry("自定义资源名")) {
// 务必保证finally会被执行
try {
//被保护的逻辑
} finally {
SphO.exit();
}
} else {
// 处理被流控的逻辑
}
4.通过注解的方式,上面也已经提过了,不过这里添加blockHandler和fallback的说明,需要区别:
- blockHandler会在原方法被限流/降级/系统保护的时候调用(异常: BlockException)
- fallback会在所有异常的适合调用
@SentinelResource(value = "自定义资源名", blockHandler = "protect", fallback = "")
public void helloWorld(String id) {
// 被保护的逻辑
}
public void protect(String id, BlockException ex) {
//原方法被限流/降级/系统保护的适合调用
}
5.Sentinel还支持异步调用
try {
AsyncEntry entry = SphU.asyncEntry(resourceName);
// 异步调用
doAsync(() -> {
try {
// 在此处处理异步调用的结果.
} finally {
entry.exit();
}
});
} catch (BlockException ex) {
// 处理被流控的逻辑
}
在Sentinel的世界中,同步和异步的资源都是同级的,而不是嵌套关系,如果你想要在异步调用中嵌套调用同步资源,官方也给出了案例
- 首先是一个同步资源的声明
public void handleResult(String result) {
Entry entry = null;
try {
entry = SphU.entry("handleResultForAsync");
} catch (BlockException ex) {
// 处理被流控的逻辑
} finally {
if (entry != null) {
entry.exit();
}
}
}
public void someAsync() {
try {
AsyncEntry entry = SphU.asyncEntry(resourceName);
// 异步执行
doAsync(userId, result -> {
// 在异步回调中进行上下文变换,通过 AsyncEntry 的 getAsyncContext 方法获取异步 Context,进行上下文切换
ContextUtil.runOnContext(entry.getAsyncContext(), () -> {
try {
// 切换后即可嵌套正常的资源调用.
handleResult(result);
} finally {
entry.exit();
}
});
});
} catch (BlockException ex) {
// 处理被流控的逻辑
}
}
接下来来到规则的部分
在Sentinel中的所有规则,都是存在于内存中,可随时查找以及修改的,并且修改后可以立即生效。
规则可以主要分为以下五大类:
- 流量控制规则
- 熔断降级规则
- 系统保护规则
- 来源访问控制规则
- 热点参数规则
详细介绍放在后面的章节来介绍,来看一个简单的流量控制的代码
private void initFlowQpsRule() {
List rules = new ArrayList<>();
FlowRule rule = new FlowRule(resourceName);
rule.setCount(20); //设置QPS数值
rule.setGrade(RuleConstant.FLOW_GRADE_QPS); //设置限流类型,QPS或并发线程数模式
rules.add(rule);
FlowRuleManager.loadRules(rules);
}
来跑一个例子把:
- 将依赖添加到pon.xml中
com.alibaba.csp
sentinel-core
1.7.0
- 运行以下代码
public class RookieStartSingle {
public static void main(String[] args) {
initFlowRule();
RookieStart start = new RookieStart();
while (true) {
try (Entry entry = SphU.entry("helloSentinel")) {
//被保护的资源
System.out.println("helloSentinel");
} catch (BlockException e) {
//限流后做的处理
System.out.println("block!");
}
}
}
private static void initFlowRule() {
List rules = new ArrayList<>();
//制定规则
FlowRule rule1 = new FlowRule();
rule1.setResource("helloSentinel");
rule1.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule1.setCount(100);
rules.add(rule1);
//规则加载
FlowRuleManager.loadRules(rules);
}
}