若您需要对特定的方法或者代码块进行流控,可以使用定义资源来实现。AHAS提供了5种定义资源的方法,定义资源后,在AHAS控制台为应用配置相应规则即可生效。
背景信息
AHAS是围绕着资源来工作的。编码的时,只需关注如何定义资源,即哪些方法或代码块可能需要保护,而无需关注这个资源要如何保护。可通过定义资源来实现对代码块的流控,定义资源方式如下:
- 注解方式定义资源
- 抛出异常的方式定义资源
- 返回布尔值方式定义资源
- 异步调用支持
- 主流框架的默认适配
方式一:注解方式定义资源
通过@SentinelResource
注解定义资源并配置blockHandler
和fallback
函数来进行限流之后的处理。
示例:
// 原本的业务方法.
@SentinelResource(blockHandler = "blockHandlerForGetUser")
public User getUserById(String id) {
throw new RuntimeException("getUserById command failed");
}
// blockHandler 函数,原方法调用被限流/降级/系统保护的时候调用
public User blockHandlerForGetUser(String id, BlockException ex) {
return new User("admin");
}
blockHandler
函数会在方法触发限流、降级或系统保护规则的时候调用,而fallback
函数仅会在原方法被降级时作为Fallback方法,其它时候不会被调用。更多信息请参见Sentinel 注解支持文档。
方式二:抛出异常的方式定义资源
使用抛出异常的方式定义资源后,当资源发生了限流之后会抛出BlockException
。您可以按需捕捉异常,并进行限流之后的逻辑处理。示例代码如下:
Entry entry = null;
// 务必保证finally会被执行
try {
// 资源名可使用任意有业务语义的字符串
entry = SphU.entry("自定义资源名");
// 被保护的业务逻辑
// do something...
} catch (BlockException ex) {
// 资源访问阻止,被限流或被降级
// 进行相应的处理操作
} finally {
if (entry != null) {
entry.exit();
}
}
SphU.entry(xxx)
需要与entry.exit()
方法需匹配调用,否则会导致调用链记录异常,抛出ErrorEntryFreeException
异常。方式三:返回布尔值方式定义资源
使用返回布尔值方式定义资源方式后,当资源发生了限流之后会返回false
。可以根据返回值,进行限流之后的逻辑处理。示例代码如下:
// 资源名可使用任意有业务语义的字符串
if (SphO.entry("自定义资源名")) {
// 务必保证finally会被执行
try {
/**
* 被保护的业务逻辑
*/
} finally {
SphO.exit();
}
} else {
// 资源访问阻止,被限流或被降级
// 进行相应的处理操作
}
方式四:异步调用支持
AHAS支持异步调用链路的统计。在异步调用中,需要通过SphU.asyncEntry(xxx)
方法定义资源,并通常需要在异步的回调函数中调用exit
方法。示例如下:
try {
AsyncEntry entry = SphU.asyncEntry(resourceName);
// 异步调用
doAsync(userId, result -> {
try {
// 在此处处理异步调用的结果
} finally {
// 在回调结束后 exit
entry.exit();
}
});
} catch (BlockException ex) {
// Request blocked
// Handle the exception (e.g. retry or fallback)
}
SphU.asyncEntry(xxx)
不会影响当前调用线程的Context,因此以下两个entry在调用链上是平级关系(处于同一层),而不是嵌套关系:
// 调用链类似于:
// -parent
// ---asyncResource
// ---syncResource
asyncEntry = SphU.asyncEntry(asyncResource);
entry = SphU.entry(normalResource);
若在异步回调中需要嵌套其它的资源调用(无论是entry
还是asyncEntry
),只需要借助Sentinel提供的上下文切换功能,在对应的地方通过ContextUtil.runOnContext(context, f)
进行Context变换,将对应资源调用处的Context切换为生成的异步Context,即可维持正确的调用链路关系。示例如下:
public void handleResult(String result) {
Entry entry = null;
try {
entry = SphU.entry("handleResultForAsync");
// Handle your result here
} catch (BlockException ex) {
// Blocked for the result handler
} finally {
if (entry != null) {
entry.exit();
}
}
}
public void someAsync() {
try {
AsyncEntry entry = SphU.asyncEntry(resourceName);
// Asynchronous invocation
doAsync(userId, result -> {
// 在异步回调中进行上下文变换,通过 AsyncEntry 的 getAsyncContext 方法获取异步 Context
ContextUtil.runOnContext(entry.getAsyncContext(), () -> {
try {
// 此处嵌套正常的资源调用
handleResult(result);
} finally {
entry.exit();
}
});
});
} catch (BlockException ex) {
// Request blocked
// Handle the exception (e.g. retry or fallback)
}
}
此时的调用链如下:
-parent
---asyncInvocation
-----handleResultForAsync
普通资源与异步资源之间嵌套示例请参见AsyncEntryDemo.java。
方式五:主流框架的默认适配
为了减少开发的复杂程度,AHAS对主流框架进行了适配,详情请参见支持组件列表。使用时只需要引入任意一个对应的SDK依赖,它们框架的方法和服务都会自动被定义为资源,无需修改现有代码。