1. 程式人生 > 其它 >Alibaba微服務元件 - Sentinel(三)

Alibaba微服務元件 - Sentinel(三)

3. Sentinel快速開始(API實現)

文件地址:https://github.com/alibaba/Sentinel/wiki/如何使用
在官方文件中,定義的Sentinel進行資源保護的幾個步驟:
1. 定義資源
2. 定義規則
3. 檢驗規則是否生效
sentinel可以自己作為一個元件在分散式專案中使用,我們先來實現sentinel-core使用

3.1 引入依賴

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.2.RELEASE</version>
        <relativePath/>
    </parent>


    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>sentinel-demo</artifactId>
    <version>1.0-SNAPSHOT</version>


    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--sentinel核心庫-->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-core</artifactId>
            <version>1.8.0</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.18</version>
        </dependency>

        <!--如果要使用@SentinelResource-->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-annotation-aspectj</artifactId>
            <version>1.8.0</version>
        </dependency>

        <!--整合sentinel控制檯-->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-transport-simple-http</artifactId>
            <version>1.8.0</version>
        </dependency>

    </dependencies>


</project>

3.2 配置bean SentinelResourceAspect

(單獨使用的話必須注入bean,這個切面沒有定義@Component)

package com.xiexie.sentinel;

import com.alibaba.csp.sentinel.annotation.aspectj.SentinelResourceAspect;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

/**
 * @Description
 * @Date 2022-04-13 9:11
 * @Author xie
 */
@SpringBootApplication
public class HelloApplication {

    public static void main(String[] args) {
        SpringApplication.run(HelloApplication.class, args);
    }

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

3.3 編寫測試邏輯

缺點:

  • 業務侵入性很強,需要在controller中寫入非業務程式碼.
  • 配置不靈活 若需要新增新的受保護資源 需要手動新增 init方法來新增流控規則

演示流控規則

package com.xiexie.sentinel.controller;

import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.Tracer;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.xiexie.sentinel.Entry.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * @Description 演示流控規則(一般放在服務消費端)
 * @Date 2022-04-13 9:10
 * @Author xie
 */
@RestController
@Slf4j
public class FlowRuleController {

    // resource name 需要跟介面地址相同(通過介面地址定義資源名稱)
    // 資源名可使用任意有業務語義的字串,比如方法名、介面名或其它可唯一標識的字串。
    private static final String RESOURCE_NAME = "hello";
    private static final String USER_RESOURCE_NAME = "user";
    private static final String DEGRADE_RESOURCE_NAME = "degrade";

    /**
     * sentinel進行流控(硬編碼形式)
     * @return java.lang.String
     */
    @RequestMapping("/hello")
    public String hello() {

        Entry entry = null;
        // sentinel針對資源進行限制
        try {
            entry = SphU.entry(RESOURCE_NAME);
            // 被保護的業務邏輯
            String str = "hello sentinel";
            log.info("=====" + str + "=====");
            return str;
        } catch (BlockException e) {
            e.printStackTrace();
            // 資源訪問阻止,被限流或者被降級
            // 進行相應的業務處理
            log.info("被限流了。。。");
            return "被限流了。。。";
        } catch (Exception e) {
            // 若需要配置降級規則,需要用這種方式記錄業務異常
            Tracer.traceEntry(e, entry);
        } finally {
            if (Objects.nonNull(entry)) {
                entry.exit();
            }
        }
        return null;
    }


    /**
     * 定義流控規則
     *
     * @date 2022/4/13 9:34
     */
    @PostConstruct // 相當於bean的 init-method
    private void initFlowRules() {

        // 流控規則集合
        // 還有其他規則道理相同,比如:熔斷降級規則 (DegradeRule)、系統保護規則 (SystemRule)、訪問控制規則 (AuthorityRule)、熱點規則 (ParamFlowRule)
        List<FlowRule> flowRuleList = new ArrayList();

        // 定義流控規則
        FlowRule flowRule1 = new FlowRule();
        // 設定受保護的資源
        flowRule1.setResource(RESOURCE_NAME);
        // 設定流控規則 限流閾值型別,QPS 模式(1)或併發執行緒數模式(0)
        flowRule1.setGrade(RuleConstant.FLOW_GRADE_QPS);
        // 設定保護限流閾值(整合起來就是1s 只能訪問一次)
        flowRule1.setCount(1);
        flowRuleList.add(flowRule1);


        // 通過@SentinelResource來定義資源的流控和降級規則
        FlowRule flowRule2 = new FlowRule();
        // 設定受保護的資源
        flowRule2.setResource(USER_RESOURCE_NAME);
        // 設定流控規則 限流閾值型別,QPS 模式(1)或併發執行緒數模式(0)
        flowRule2.setGrade(RuleConstant.FLOW_GRADE_QPS);
        // 設定保護限流閾值(整合起來就是1s 只能訪問一次)
        flowRule2.setCount(1);
        flowRuleList.add(flowRule2);


        // 載入配置好的規則
        FlowRuleManager.loadRules(flowRuleList);

    }

    /********************************* 使用@SentinelResource改善硬編碼格式 ****************************/

    /**
     * @SentinelResource 改善介面中資源定義和被流控降級後的處理方法
     * 使用:
     * 1. 新增依賴:<artifactId>sentinel-annotation-aspectj</artifactId>
     * 2. 配置bean SentinelResourceAspect(單獨使用的話必須注入bean,這個切面沒有定義@Component)
     *    value:定義資源
     *    blockHandler:設定流控降級後的處理方法(預設該方法必須跟流控介面宣告在同一個類中)
     *      如果不想在同一個類中可以用blockHandlerClass來指定,但是方法必須是public static修飾
     *    fallback:當接口出現了異常,就可以交給fallback執行的方法進行處理
     *      如果不想在同一個類中可以用fallbackClass來指定,但是方法必須是public static修飾
     *    優先順序:blockHandler > fallback
     *    exceptionsToIgnore:要排除的異常
     *
     * @param id
     * @return com.xiexie.sentinel.Entry.User
     */
    @RequestMapping("/user")
    @SentinelResource(value = USER_RESOURCE_NAME
                    , blockHandler = "blockHandlerForGetUser"
                    , blockHandlerClass = {User.class}
                    , fallback = "fallbackForGetUser"
                    , exceptionsToIgnore = {ArithmeticException.class}
    )
    public User getUser(String id) {
        //int a = 1/0;
        return new User("xiexie");
    }

    /**
     * 注意:
     * 1. 一定要是public修飾
     * 2. 返回值一定要跟源方法一致,包含源方法引數(順序也要一致)
     * 3. 可以在引數中加BlockException異常處理,可以區分是什麼型別的規則,對症下藥。
     * @param id
     * @param e
     * @return com.xiexie.sentinel.Entry.User
     */
    public User blockHandlerForGetUser(String id, BlockException e) {
        e.printStackTrace();
        return new User("被流控");
    }

    public User fallbackForGetUser(String id, Throwable throwable) {
        throwable.printStackTrace();
        return new User("異常處理");
    }


}

演示熔斷降級規則

package com.xiexie.sentinel.controller;

import com.alibaba.csp.sentinel.EntryType;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.xiexie.sentinel.Entry.User;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;

/**
 * @Description 演示熔斷降級規則(一般放在服務提供端)
 * @Date 2022-04-13 11:21
 * @Author xie
 */
@RestController
public class DegradeRuleController {

    private static final String DEGRADE_RESOURCE_NAME = "degrade";


    @RequestMapping("/degrade")
    @SentinelResource(value = DEGRADE_RESOURCE_NAME, entryType = EntryType.IN, blockHandler = "blockHandlerForDegrade")
    public User degrade(String id) {
        // 異常數 比例
        throw new RuntimeException("異常了");
    }

    public User blockHandlerForDegrade(String id, BlockException e) {
        e.printStackTrace();
        return new User("觸發熔斷, 開始降級操作");
    }

    /**
     * 初始化熔斷降級規則
     * @date 2022/4/13 11:22
     */
    @PostConstruct
    public void initDegradeRule() {

        // 降級規則異常
        List<DegradeRule> degradeRuleList = new ArrayList();

        DegradeRule degradeRule = new DegradeRule();
        degradeRule.setResource(DEGRADE_RESOURCE_NAME);
        // 熔斷降級策略 支援慢呼叫比例/異常比例/異常數策略(預設值慢呼叫比例)
        degradeRule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT);
        // 觸發熔斷異常數 2個
        degradeRule.setCount(2);
        // 觸發熔斷最小請求數 2次
        degradeRule.setMinRequestAmount(2);
        // 統計時長 (時間太短不好測試)1分鐘 單位ms   預設1s
        degradeRule.setStatIntervalMs(60 * 1000);

        // --- 上述條件就是:一分鐘內 請求了兩次+  出現了兩次異常 就會觸發熔斷

        // 熔斷時長 單位s
        // 具體說明:一旦觸發了熔斷,再次請求介面就會直接呼叫降級方法,而不是介面本身
        //         10s過後進入半開狀態,恢復介面本身呼叫,如果第一次請求就出現了異常,再次熔斷,不會根據設定的條件進行判定
        //         然後繼續重複上述操作。
        degradeRule.setTimeWindow(10);

        degradeRuleList.add(degradeRule);
        DegradeRuleManager.loadRules(degradeRuleList);
    }
}

3.4 @SentinelResource註解實現

@SentinelResource 註解用來標識資源是否被限流、降級。
blockHandler:  定義當資源內部發生了BlockException應該進入的方法(捕獲的是Sentinel定義的異常)
fallback:  定義的是資源內部發生了Throwable應該進入的方法
exceptionsToIgnore:配置fallback可以忽略的異常
原始碼入口:com.alibaba.csp.sentinel.annotation.aspectj.SentinelResourceAspect

// 程式碼如上

3.5 規則的種類

  • 流量控制規則
  • 熔斷降級規則
  • 系統保護規則
  • 訪問控制規則
  • 熱點規則

剩下規則演示請檢視官方文件,基本是一樣的操作https://github.com/alibaba/Sentinel/wiki/如何使用

3.6 如果需要控制檯展示

控制檯官方文件:https://github.com/alibaba/Sentinel/wiki/控制檯

客戶端需要引入 Transport 模組來與 Sentinel 控制檯進行通訊

<!--整合sentinel控制檯-->
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-transport-simple-http</artifactId>
    <version>1.8.0</version>
</dependency>

並且設定JVM引數

‐Dcsp.sentinel.dashboard.server=youConsoleIp:port