若您需要对特定的方法或者代码块进行流控,可以使用定义资源来实现。AHAS 提供了 5 种定义资源的方法,定义资源后,在 AHAS 控制台为应用配置相应规则即可生效。

背景信息

AHAS 是围绕着资源来工作的。编码的时,只需关注如何定义资源,即哪些方法或代码块可能需要保护,而无需关注这个资源要如何保护。可通过定义资源来实现对代码块的流控,定义资源方式如下:

  • 注解方式定义资源
  • 抛出异常的方式定义资源
  • 返回布尔值方式定义资源
  • 异步调用支持
  • 主流框架的默认适配

方式一:注解方式定义资源

通过 @SentinelResource 注解定义资源并配置 blockHandlerfallback 函数来进行限流之后的处理。

示例:

// 原本的业务方法.
@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 对主流框架进行了适配,详情请参见支持列表。使用时只需要引入对应的依赖,它们框架的方法和服务都会自动被定义为资源,无需修改现有代码。