定义资源

若您需要对特定的方法或者代码块进行流控,可以使用定义资源来实现。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对主流框架进行了适配,详情请参见支持组件列表。使用时只需要引入任意一个对应的SDK依赖,它们框架的方法和服务都会自动被定义为资源,无需修改现有代码。