1. 程式人生 > >Spring Cloud Alibaba | Sentinel: 服務限流基礎篇

Spring Cloud Alibaba | Sentinel: 服務限流基礎篇

目錄

  • Spring Cloud Alibaba | Sentinel: 服務限流基礎篇
    • 1. 簡介
    • 2. 定義資源
      • 2.1 主流框架的預設適配
      • 2.2 丟擲異常的方式定義資源
      • 2.3 返回布林值方式定義資源
      • 2.4 註解方式定義資源
      • 2.5 非同步呼叫支援
    • 3. 規則的種類
      • 3.1 流量控制規則 (FlowRule)
      • 3.2 熔斷降級規則 (DegradeRule)
      • 3.3 系統保護規則 (SystemRule)
      • 3.4 訪問控制規則 (AuthorityRule)

Spring Cloud Alibaba | Sentinel: 服務限流基礎篇

Springboot: 2.1.6.RELEASE

SpringCloud: Greenwich.SR1

如無特殊說明,本系列文章全採用以上版本

上一篇《Spring Cloud Alibaba | Sentinel: 分散式系統的流量防衛兵初探》我們介紹了Sentinel是什麼,並且做了一個簡單的Demo來驗證服務限流。

這一篇,我們主要講什麼是服務限流,包括Sentinel支援的限流方式。

在講服務限流之前,我們先了解什麼是資源。

1. 簡介

資源:可以是任何東西,服務,服務裡的方法,甚至是一段程式碼。使用 Sentinel 來進行資源保護,主要分為幾個步驟:

  1. 定義資源

  2. 定義規則

  3. 檢驗規則是否生效

先把可能需要保護的資源定義好,之後再配置規則。也可以理解為,只要有了資源,我們就可以在任何時候靈活地定義各種流量控制規則。在編碼的時候,只需要考慮這個程式碼是否需要保護,如果需要保護,就將之定義為一個資源。

2. 定義資源

2.1 主流框架的預設適配

為了減少開發的複雜程度,Sentinel對大部分的主流框架,例如 Web Servlet、Dubbo、Spring Cloud、gRPC、Spring WebFlux、Reactor 等都做了適配。

2.1.1 Web Servlet

Sentinel 提供與 Servlet 的整合,可以對 Web 請求進行流量控制。使用時需引入以下模組(以 Maven 為例):

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-web-servlet</artifactId>
    <version>x.y.z</version>
</dependency>

僅需要在 Web 容器中的 web.xml 配置檔案中進行如下配置即可開啟 Sentinel 支援:

<filter>
    <filter-name>SentinelCommonFilter</filter-name>
    <filter-class>com.alibaba.csp.sentinel.adapter.servlet.CommonFilter</filter-class>
</filter>

<filter-mapping>
    <filter-name>SentinelCommonFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

若是 Spring 應用可以通過 Spring 進行配置,例如:

@Configuration
public class FilterConfig {

  @Bean
  public FilterRegistrationBean sentinelFilterRegistration() {
    FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<>();
    registration.setFilter(new CommonFilter());
    registration.addUrlPatterns("/*");
    registration.setName("sentinelFilter");
    registration.setOrder(1);

    return registration;
  }
}

預設情況下,當請求被限流時會返回預設的提示頁面。您也可以通過 WebServletConfig.setBlockPage(blockPage) 方法設定自定義的跳轉 URL,當請求被限流時會自動跳轉至設定好的 URL。同樣也可以實現 UrlBlockHandler 介面並編寫定製化的限流處理邏輯,然後將其註冊至 WebCallbackManager 中。

注意: Sentinel Web Filter 會將每個到來的不同的 URL 都作為不同的資源處理,因此對於 REST 風格的 API,需要自行實現 UrlCleaner 介面清洗一下資源(比如將滿足 /foo/:id 的 URL 都歸到 /foo/* 資源下),然後將其註冊至 WebCallbackManager 中。否則會導致資源數量過多,超出資源數量閾值(目前是 6000)時多出的資源的規則將 不會生效。

2.1.2 Dubbo

Sentinel 提供 Dubbo 的相關適配 Sentinel Dubbo Adapter,主要包括針對 Service Provider 和 Service Consumer 實現的 Filter。相關模組:

  • sentinel-apache-dubbo-adapter(相容 Apache Dubbo 2.7.x 及以上版本,自 Sentinel 1.5.1 開始支援)

  • sentinel-dubbo-adapter(相容 Dubbo 2.6.x 版本)

對於 Apache Dubbo 2.7.x 及以上版本,使用時需引入以下模組(以 Maven 為例):

<dependency>
  <groupId>com.alibaba.csp</groupId>
  <artifactId>sentinel-apache-dubbo-adapter</artifactId>
  <version>x.y.z</version>
</dependency>

對於 Dubbo 2.6.x 及以下版本,使用時需引入以下模組(以 Maven 為例):

<dependency>
  <groupId>com.alibaba.csp</groupId>
  <artifactId>sentinel-dubbo-adapter</artifactId>
  <version>x.y.z</version>
</dependency>

引入此依賴後,Dubbo 的服務介面和方法(包括呼叫端和服務端)就會成為 Sentinel 中的資源,在配置了規則後就可以自動享受到 Sentinel 的防護能力。

若不希望開啟 Sentinel Dubbo Adapter 中的某個 Filter,可以手動關閉對應的 Filter,比如:

<!-- 關閉 Sentinel 對應的 Service Consumer Filter -->
<dubbo:consumer filter="-sentinel.dubbo.consumer.filter"/>

限流粒度可以是服務介面和服務方法兩種粒度:

  • 服務介面:resourceName 為 介面全限定名,如 com.alibaba.csp.sentinel.demo.dubbo.FooService

  • 服務方法:resourceName 為 介面全限定名:方法簽名,如 com.alibaba.csp.sentinel.demo.dubbo.FooService:sayHello(java.lang.String)

2.1.3 Spring Cloud

引入依賴:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

下面這個例子就是一個最簡單的使用 Sentinel 的例子:

@SpringBootApplication
public class Application {
  public static void main(String[] args) {
    SpringApplication.run(ServiceApplication.class, args);
  }
}

@RestController
public class TestController {
  @GetMapping(value = "/hello")
  @SentinelResource("hello")
  public String hello() {
    return "Hello Sentinel";
  }
}

@SentinelResource 註解用來標識資源是否被限流、降級。上述例子上該註解的屬性 'hello' 表示資源名。

@SentinelResource 用於定義資源,並提供可選的異常處理和 fallback 配置項。 @SentinelResource 註解包含以下屬性:

  • value:資源名稱,必需項(不能為空)

  • entryTypeentry 型別,可選項(預設為 EntryType.OUT

  • blockHandler / blockHandlerClass: blockHandler 對應處理 BlockException 的函式名稱,可選項。blockHandler 函式訪問範圍需要是 public,返回型別需要與原方法相匹配,引數型別需要和原方法相匹配並且最後加一個額外的引數,型別為 BlockExceptionblockHandler 函式預設需要和原方法在同一個類中。若希望使用其他類的函式,則可以指定 blockHandlerClass 為對應的類的 Class 物件,注意對應的函式必需為 static 函式,否則無法解析。

  • fallbackfallback 函式名稱,可選項,用於在丟擲異常的時候提供 fallback 處理邏輯。fallback 函式可以針對所有型別的異常(除了 exceptionsToIgnore 裡面排除掉的異常型別)進行處理。fallback 函式簽名和位置要求:

    • 返回值型別必須與原函式返回值型別一致;

    • 方法引數列表需要和原函式一致,或者可以額外多一個 Throwable 型別的引數用於接收對應的異常;

    • fallback 函式預設需要和原方法在同一個類中。若希望使用其他類的函式,則可以指定 fallbackClass 為對應的類的 Class 物件,注意對應的函式必需為 static 函式,否則無法解析。

  • defaultFallback(since 1.6.0):預設的 fallback 函式名稱,可選項,通常用於通用的 fallback 邏輯(即可以用於很多服務或方法)。預設 fallback 函式可以針對所有型別的異常(除了 exceptionsToIgnore 裡面排除掉的異常型別)進行處理。若同時配置了 fallbackdefaultFallback,則只有 fallback 會生效。defaultFallback 函式簽名要求:

    • 返回值型別必須與原函式返回值型別一致;

    • 方法引數列表需要為空,或者可以額外多一個 Throwable 型別的引數用於接收對應的異常。

    • defaultFallback 函式預設需要和原方法在同一個類中。若希望使用其他類的函式,則可以指定 fallbackClass 為對應的類的 Class 物件,注意對應的函式必需為 static 函式,否則無法解析。

  • exceptionsToIgnore(since 1.6.0):用於指定哪些異常被排除掉,不會計入異常統計中,也不會進入 fallback 邏輯中,而是會原樣丟擲。

特別地,若 blockHandlerfallback 都進行了配置,則被限流降級而丟擲 BlockException 時只會進入 blockHandler 處理邏輯。若未配置 blockHandlerfallbackdefaultFallback,則被限流降級時會將 BlockException 直接丟擲。

示例:

public class TestService {

  // 對應的 `handleException` 函式需要位於 `ExceptionUtil` 類中,並且必須為 static 函式.
  @SentinelResource(value = "test", blockHandler = "handleException", blockHandlerClass = {ExceptionUtil.class})
  public void test() {
    System.out.println("Test");
  }

  // 原函式
  @SentinelResource(value = "hello", blockHandler = "exceptionHandler", fallback = "helloFallback")
  public String hello(long s) {
    return String.format("Hello at %d", s);
  }
  
  // Fallback 函式,函式簽名與原函式一致或加一個 Throwable 型別的引數.
  public String helloFallback(long s) {
    return String.format("Halooooo %d", s);
  }

  // Block 異常處理函式,引數最後多一個 BlockException,其餘與原函式一致.
  public String exceptionHandler(long s, BlockException ex) {
    // Do some log here.
    ex.printStackTrace();
    return "Oops, error occurred at " + s;
  }
}

從 1.4.0 版本開始,註解方式定義資源支援自動統計業務異常,無需手動呼叫 Tracer.trace(ex) 來記錄業務異常。Sentinel 1.4.0 以前的版本需要自行呼叫 Tracer.trace(ex) 來記錄業務異常。

AspectJ

如果應用直接使用了 AspectJ,那麼需要在 aop.xml 檔案中引入對應的 Aspect:

<aspects>
  <aspect name="com.alibaba.csp.sentinel.annotation.aspectj.SentinelResourceAspect"/>
</aspects>

Spring AOP

如果應用使用了 Spring AOP,需要通過配置的方式將 SentinelResourceAspect 註冊為一個 Spring Bean:

@Configuration
public class SentinelAspectConfiguration {
  @Bean
  public SentinelResourceAspect sentinelResourceAspect() {
      return new SentinelResourceAspect();
  }
}

2.1.4 Spring WebFlux

注:從 1.5.0 版本開始支援,需要 Java 8 及以上版本。

Sentinel 提供與 Spring WebFlux 的整合模組,從而 Reactive Web 應用也可以利用 Sentinel 的流控降級來保障穩定性。該整合模組基於 Sentinel Reactor Adapter 實現。

使用時需引入以下模組(以 Maven 為例):

<dependency>
  <groupId>com.alibaba.csp</groupId>
  <artifactId>sentinel-spring-webflux-adapter</artifactId>
  <version>x.y.z</version>
</dependency>

使用時只需注入對應的 SentinelWebFluxFilter 例項以及 SentinelBlockExceptionHandler 例項即可。比如:

@Configuration
public class WebFluxConfig {

  private final List<ViewResolver> viewResolvers;
  private final ServerCodecConfigurer serverCodecConfigurer;

  public WebFluxConfig(ObjectProvider<List<ViewResolver>> viewResolversProvider,
                        ServerCodecConfigurer serverCodecConfigurer) {
    this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
    this.serverCodecConfigurer = serverCodecConfigurer;
  }

  @Bean
  @Order(-1)
  public SentinelBlockExceptionHandler sentinelBlockExceptionHandler() {
    // Register the block exception handler for Spring WebFlux.
    return new SentinelBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
  }

  @Bean
  @Order(-1)
  public SentinelWebFluxFilter sentinelWebFluxFilter() {
    // Register the Sentinel WebFlux filter.
    return new SentinelWebFluxFilter();
  }
}

您可以在 WebFluxCallbackManager 註冊回撥進行定製:

  • setBlockHandler:註冊函式用於實現自定義的邏輯處理被限流的請求,對應介面為 BlockRequestHandler。預設實現為 DefaultBlockRequestHandler,當被限流時會返回類似於下面的錯誤資訊:Blocked by Sentinel: FlowException

  • setUrlCleaner:註冊函式用於 Web 資源名的歸一化。函式型別為 (ServerWebExchange, String) → String,對應含義為 (webExchange, originalUrl) → finalUrl

  • setRequestOriginParser:註冊函式用於從請求中解析請求來源。函式型別為 ServerWebExchange → String

2.1.5 gRPC

Sentinel 提供與 gRPC Java 的整合,以 gRPC ServerInterceptor 和 ClientInterceptor 的形式保護 gRPC 服務資源。

使用時需引入以下模組(以 Maven 為例):

<dependency>
  <groupId>com.alibaba.csp</groupId>
  <artifactId>sentinel-grpc-adapter</artifactId>
  <version>x.y.z</version>
</dependency>

在使用 Sentinel gRPC Adapter 時,只需要將對應的 Interceptor 註冊至對應的客戶端或服務端中。其中客戶端的示例如下:

public class ServiceClient {
  private final ManagedChannel channel;
  ServiceClient(String host, int port) {
    this.channel = ManagedChannelBuilder.forAddress(host, port)
        .intercept(new SentinelGrpcClientInterceptor()) // 在此處註冊攔截器
        .build();
    // 在此處初始化客戶端 stub 類
  }
}

服務端的示例如下:

import io.grpc.Server;

Server server = ServerBuilder.forPort(port)
     .addService(new MyServiceImpl()) // 新增自己的服務實現
     .intercept(new SentinelGrpcServerInterceptor()) // 在此處註冊攔截器
     .build();

注意:由於 gRPC 攔截器中 ClientCall/ServerCall 以回撥的形式進行請求響應資訊的獲取,每次 gRPC 服務呼叫計算出的 RT 可能會不準確。Sentinel gRPC Adapter 目前只支援 unary call。

2.1.6 Reactive

注:從 1.5.0 版本開始支援,需要 Java 8 及以上版本。

Sentinel 提供 Reactor 的適配,可以方便地在 reactive 應用中接入 Sentinel。

使用時需引入以下模組(以 Maven 為例):

<dependency>
  <groupId>com.alibaba.csp</groupId>
  <artifactId>sentinel-reactor-adapter</artifactId>
  <version>x.y.z</version>
</dependency>

Sentinel Reactor Adapter 分別針對 Mono 和 Flux 實現了對應的 Sentinel Operator,從而在各種事件觸發時匯入 Sentinel 的相關邏輯。同時 Sentinel 在上層提供了 SentinelReactorTransformer 用於在組裝期裝入對應的 operator,使用者使用時只需要通過 transform 操作符來進行變換即可。

接入示例:

someService.doSomething() // return type: Mono<T> or Flux<T>
   .transform(new SentinelReactorTransformer<>(resourceName)) // 在此處進行變換
   .subscribe();

2.1.7 API Gateway

Sentinel 支援對 Spring Cloud Gateway、Zuul 等主流的 API Gateway 進行限流。

Spring Cloud Gateway

從 1.6.0 版本開始,Sentinel 提供了 Spring Cloud Gateway 的適配模組,可以提供兩種資源維度的限流:

  • route 維度:即在 Spring 配置檔案中配置的路由條目,資源名為對應的 routeId

  • 自定義 API 維度:使用者可以利用 Sentinel 提供的 API 來自定義一些 API 分組

使用時需引入以下模組(以 Maven 為例):

<dependency>
  <groupId>com.alibaba.csp</groupId>
  <artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
  <version>x.y.z</version>
</dependency>

使用時只需注入對應的 SentinelGatewayFilter 例項以及 SentinelGatewayBlockExceptionHandler 例項即可。比如:

@Configuration
public class GatewayConfiguration {

    private final List<ViewResolver> viewResolvers;
    private final ServerCodecConfigurer serverCodecConfigurer;

    public GatewayConfiguration(ObjectProvider<List<ViewResolver>> viewResolversProvider,
                                ServerCodecConfigurer serverCodecConfigurer) {
        this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
        this.serverCodecConfigurer = serverCodecConfigurer;
    }

    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
        // Register the block exception handler for Spring Cloud Gateway.
        return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
    }

    @Bean
    @Order(-1)
    public GlobalFilter sentinelGatewayFilter() {
        return new SentinelGatewayFilter();
    }
}

因為Sentinel暫時只支援了Zuul1.x,具體用發這裡暫不介紹。

2.1.8 Apache RocketMQ

在 Apache RocketMQ 中,當消費者去消費訊息的時候,無論是通過 pull 的方式還是 push 的方式,都可能會出現大批量的訊息突刺。如果此時要處理所有訊息,很可能會導致系統負載過高,影響穩定性。但其實可能後面幾秒之內都沒有訊息投遞,若直接把多餘的訊息丟掉則沒有充分利用系統處理訊息的能力。我們希望可以把訊息突刺均攤到一段時間內,讓系統負載保持在訊息處理水位之下的同時儘可能地處理更多訊息,從而起到“削峰填谷”的效果:

上圖中紅色的部分代表超出訊息處理能力的部分。我們可以看到訊息突刺往往都是瞬時的、不規律的,其後一段時間系統往往都會有空閒資源。我們希望把紅色的那部分訊息平攤到後面空閒時去處理,這樣既可以保證系統負載處在一個穩定的水位,又可以儘可能地處理更多訊息。Sentinel 專門為這種場景提供了勻速器的特性,可以把突然到來的大量請求以勻速的形式均攤,以固定的間隔時間讓請求通過,以穩定的速度逐步處理這些請求,起到“削峰填谷”的效果,從而避免流量突刺造成系統負載過高。同時堆積的請求將會排隊,逐步進行處理;當請求排隊預計超過最大超時時長的時候則直接拒絕,而不是拒絕全部請求。

比如在 RocketMQ 的場景下配置了勻速模式下請求 QPS 為 5,則會每 200 ms 處理一條訊息,多餘的處理任務將排隊;同時設定了超時時間為 5 s,預計排隊時長超過 5 s 的處理任務將會直接被拒絕。示意圖如下圖所示:

RocketMQ 使用者可以根據不同的 group 和不同的 topic 分別設定限流規則,限流控制模式設定為勻速器模式(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER),比如:


private void initFlowControlRule() {
    FlowRule rule = new FlowRule();
    rule.setResource(KEY); // 對應的 key 為 `groupName:topicName`
    rule.setCount(5);
    rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
    rule.setLimitApp("default");

    // 勻速器模式下,設定了 QPS 為 5,則請求每 200 ms 允許通過 1 個
    rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER);
    // 如果更多的請求到達,這些請求會被置於虛擬的等待佇列中。等待佇列有一個 max timeout,如果請求預計的等待時間超過這個時間會直接被 block
    // 在這裡,timeout 為 5s
    rule.setMaxQueueingTimeMs(5 * 1000);
    FlowRuleManager.loadRules(Collections.singletonList(rule));
}

2.2 丟擲異常的方式定義資源

SphU 包含了 try-catch 風格的 API。用這種方式,當資源發生了限流之後會丟擲 BlockException。這個時候可以捕捉異常,進行限流之後的邏輯處理。示例程式碼如下:

// 1.5.0 版本開始可以利用 try-with-resources 特性
// 資源名可使用任意有業務語義的字串,比如方法名、介面名或其它可唯一標識的字串。
try (Entry entry = SphU.entry("resourceName")) {
  // 被保護的業務邏輯
  // do something here...
} catch (BlockException ex) {
  // 資源訪問阻止,被限流或被降級
  // 在此處進行相應的處理操作
}

特別地 ,若 entry 的時候傳入了熱點引數,那麼 exit 的時候也一定要帶上對應的引數(exit(count, args)),否則可能會有統計錯誤。這個時候不能使用 try-with-resources 的方式。另外通過 Tracer.trace(ex) 來統計異常資訊時,由於 try-with-resources 語法中 catch 呼叫順序的問題,會導致無法正確統計異常數,因此統計異常資訊時也不能在 try-with-resources 的 catch 塊中呼叫 Tracer.trace(ex)。

1.5.0 之前的版本的示例:

Entry entry = null;
// 務必保證finally會被執行
try {
  // 資源名可使用任意有業務語義的字串
  entry = SphU.entry("自定義資源名");
  // 被保護的業務邏輯
  // do something...
} catch (BlockException e1) {
  // 資源訪問阻止,被限流或被降級
  // 進行相應的處理操作
} finally {
  if (entry != null) {
    entry.exit();
  }
}

注意 : SphU.entry(xxx) 需要與 entry.exit() 方法成對出現,匹配呼叫,否則會導致呼叫鏈記錄異常,丟擲 ErrorEntryFreeException 異常。

2.3 返回布林值方式定義資源

SphO 提供 if-else 風格的 API。用這種方式,當資源發生了限流之後會返回 false,這個時候可以根據返回值,進行限流之後的邏輯處理。示例程式碼如下:

// 資源名可使用任意有業務語義的字串
if (SphO.entry("自定義資源名")) {
  // 務必保證finally會被執行
  try {
    /**
    * 被保護的業務邏輯
    */
  } finally {
    SphO.exit();
  }
} else {
  // 資源訪問阻止,被限流或被降級
  // 進行相應的處理操作
}

2.4 註解方式定義資源

Sentinel 支援通過 @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 函式會針對所有型別的異常。請注意 blockHandler 和 fallback 函式的形式要求。

2.5 非同步呼叫支援

Sentinel 支援非同步呼叫鏈路的統計。在非同步呼叫中,需要通過 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).
    }
}

3. 規則的種類

Sentinel 的所有規則都可以在記憶體態中動態地查詢及修改,修改之後立即生效。同時 Sentinel 也提供相關 API,供您來定製自己的規則策略。

Sentinel 支援以下幾種規則:流量控制規則 、熔斷降級規則 、系統保護規則 、來源訪問控制規則 和 熱點引數規則 。

3.1 流量控制規則 (FlowRule)

流量規則的定義

重要屬性:

Field 說明 預設值
resource 資源名,資源名是限流規則的作用物件
count 限流閾值
grade 限流閾值型別,QPS 或執行緒數模式 QPS 模式
limitApp 流控針對的呼叫來源 default,代表不區分呼叫來源
strategy 判斷的根據是資源自身,還是根據其它關聯資源 (refResource),還是根據鏈路入口 根據資源本身
controlBehavior 流控效果(直接拒絕 / 排隊等待 / 慢啟動模式) 直接拒絕

同一個資源可以同時有多個限流規則。

通過程式碼定義流量控制規則

理解上面規則的定義之後,我們可以通過呼叫 FlowRuleManager.loadRules() 方法來用硬編碼的方式定義流量控制規則,比如:

private void initFlowQpsRule() {
    List<FlowRule> rules = new ArrayList<>();
    FlowRule rule = new FlowRule(resourceName);
    // set limit qps to 20
    rule.setCount(20);
    rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
    rule.setLimitApp("default");
    rules.add(rule);
    FlowRuleManager.loadRules(rules);
}

3.2 熔斷降級規則 (DegradeRule)

熔斷降級規則包含下面幾個重要的屬性:

Field 說明 預設值
resource 資源名,即限流規則的作用物件
count 閾值
grade 降級模式,根據 RT 降級還是根據異常比例降級
timeWindow 降級的時間,單位為 s

同一個資源可以同時有多個降級規則。

理解上面規則的定義之後,我們可以通過呼叫 DegradeRuleManager.loadRules() 方法來用硬編碼的方式定義流量控制規則。

private void initDegradeRule() {
    List<DegradeRule> rules = new ArrayList<>();
    DegradeRule rule = new DegradeRule();
    rule.setResource(KEY);
    // set threshold RT, 10 ms
    rule.setCount(10);
    rule.setGrade(RuleConstant.DEGRADE_GRADE_RT);
    rule.setTimeWindow(10);
    rules.add(rule);
    DegradeRuleManager.loadRules(rules);
}

3.3 系統保護規則 (SystemRule)

規則包含下面幾個重要的屬性:

Field 說明 預設值
highestSystemLoad 最大的 load1,參考值 -1 (不生效)
avgRt 所有入口流量的平均響應時間 -1 (不生效)
maxThread 入口流量的最大併發數 -1 (不生效)
qps 所有入口資源的 QPS -1 (不生效)

理解上面規則的定義之後,我們可以通過呼叫 SystemRuleManager.loadRules() 方法來用硬編碼的方式定義流量控制規則。

private void initSystemRule() {
    List<SystemRule> rules = new ArrayList<>();
    SystemRule rule = new SystemRule();
    rule.setHighestSystemLoad(10);
    rules.add(rule);
    SystemRuleManager.loadRules(rules);
}

3.4 訪問控制規則 (AuthorityRule)

很多時候,我們需要根據呼叫方來限制資源是否通過,這時候可以使用 Sentinel 的訪問控制(黑白名單)的功能。黑白名單根據資源的請求來源(origin)限制資源是否通過,若配置白名單則只有請求來源位於白名單內時才可通過;若配置黑名單則請求來源位於黑名單時不通過,其餘的請求通過。

授權規則,即黑白名單規則(AuthorityRule)非常簡單,主要有以下配置項:

  • resource:資源名,即限流規則的作用物件

  • limitApp:對應的黑名單/白名單,不同 origin 用 , 分隔,如 appA,appB

  • strategy:限制模式,AUTHORITY_WHITE 為白名單模式,AUTHORITY_BLACK 為黑名單模式,預設為白名單模式