Sentinel介紹與使用


一、什么是Sentinel:Sentinel是阿里開源的項目,提供了流量控制、熔斷降級、系統負載保護等多個維度來保障服務之間的穩定性。
官網:https://github.com/alibaba/Sentinel/wiki

Sentinel主要特性:

Sentinel與Hystrix的區別

關於Sentinel與Hystrix的區別見:https://yq.aliyun.com/articles/633786/

總體來說:

Hystrix常用的線程池隔離會造成線程上下切換的overhead比較大;Hystrix使用的信號量隔離對某個資源調用的並發數進行控制,效果不錯,但是無法對慢調用進行自動降級;Sentinel通過並發線程數的流量控制提供信號量隔離的功能;

此外,Sentinel支持的熔斷降級維度更多,可對多種指標進行流控、熔斷,且提供了實時監控和控制面板,功能更為強大。

二、代碼示例
下面跟隨官網中提供的Quick Start Demo,去看看如何使用Sentinel實現限流和降級。

注意:

JDK >= 1.7;
Sentinel版本為1.7.0;
引入Sentinel jar包:

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-core</artifactId>
    <version>1.7.0</version>
</dependency>

1、限流
關於限流的使用和介紹見:Sentinel流量控制

流量控制(Flow Control),原理是監控應用流量的QPS或並發線程數等指標,當達到指定閾值時對流量進行控制,避免系統被瞬時的流量高峰沖垮,保障應用高可用性。

下面寫個簡單的示例,看看如何使用Sentinel實現限流。

首先寫個簡單的訂單查詢接口,用於后續接口限流示例:


@Component
public class OrderQueryService {

    public String queryOrderInfo(String orderId) {
        System.out.println("獲取訂單信息:" + orderId);
        return "return OrderInfo :" + orderId;
    }
}

@Controller
@RequestMapping("order")
public class OrderController {
    @Autowired
    private OrderQueryService orderQueryService;

    @RequestMapping("/getOrder")
    @ResponseBody
    public String queryOrder1(@RequestParam("orderId") String orderId) {

        return orderQueryService.queryOrderInfo(orderId);
    }
}正常情況下,調用OrderController中訂單查詢接口,會返回訂單信息,如何控制接口訪問的QPS在2以下呢?Sentienl限流提供了以下實現方式:

首先需要定義限流規則,比如對哪個接口進行限流,限制的QPS為多少,限制調用方app是什么等:

public void initFlowQpsRule() {
        List<FlowRule> rules = new ArrayList<FlowRule>();
        FlowRule rule1 = new FlowRule();
        rule1.setResource(KEY);
        // QPS控制在2以內
        rule1.setCount(2);
        // QPS限流
        rule1.setGrade(RuleConstant.FLOW_GRADE_QPS);
        rule1.setLimitApp("default");
        rules.add(rule1);
        FlowRuleManager.loadRules(rules);
    }

限流實現方式有多種,本文只列出常見兩種:

(1)限流實現方式一: 拋出異常的方式定義資源

此方式對代碼侵入性較高,需要在接口調用的地方通過try-catch風格的API對代碼進行包裝:


		/**
     * 限流實現方式一: 拋出異常的方式定義資源
     *
     * @param orderId
     * @return
     */
		@RequestMapping("/getOrder1")
    @ResponseBody
    public String queryOrder2(@RequestParam("orderId") String orderId) {

        Entry entry = null;
        // 資源名
        String resourceName = KEY;
        try {
            // entry可以理解成入口登記
            entry = SphU.entry(resourceName);
            // 被保護的邏輯, 這里為訂單查詢接口
            return orderQueryService.queryOrderInfo(orderId);
        } catch (BlockException blockException) {
            // 接口被限流的時候, 會進入到這里
            log.warn("---getOrder1接口被限流了---, exception: ", blockException);
            return "接口限流, 返回空";
        } finally {
            // SphU.entry(xxx) 需要與 entry.exit() 成對出現,否則會導致調用鏈記錄異常
            if (entry != null) {
                entry.exit();
            }
        }

    }


測試,當QPS > 2時,接口返回:

(2)限流實現方式二: 注解方式定義資源

上述通過try-catch風格的API可以實現限流,但是對代碼侵入性太高,推薦使用注解的方式來實現。下文若不做注明,默認都會采用注解的方式實現。

首先需要引入支持注解的jar包:


<dependency>
     <groupId>com.alibaba.csp</groupId>
     <artifactId>sentinel-annotation-aspectj</artifactId>
     <version>${sentinel.version}</version>
</dependency>

Sentinel切面類配置:

@Configuration
public class SentinelAspectConfiguration {

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

 

在接口OrderQueryService中,使用注解實現訂單查詢接口的限流:

    /**
     * 訂單查詢接口, 使用Sentinel注解實現限流
     *
     * @param orderId
     * @return
     */
    @SentinelResource(value = "getOrderInfo", blockHandler = "handleFlowQpsException",
            fallback = "queryOrderInfo2Fallback")
    public String queryOrderInfo2(String orderId) {

        // 模擬接口運行時拋出代碼異常
        if ("000".equals(orderId)) {
            throw new RuntimeException();
        }

        System.out.println("獲取訂單信息:" + orderId);
        return "return OrderInfo :" + orderId;
    }

    /**
     * 訂單查詢接口拋出限流或降級時的處理邏輯
     *
     * 注意: 方法參數、返回值要與原函數保持一致
     * @return
     */
    public String handleFlowQpsException(String orderId, BlockException e) {
        e.printStackTrace();
        return "handleFlowQpsException for queryOrderInfo2: " + orderId;
    }

    /**
     * 訂單查詢接口運行時拋出的異常提供fallback處理
     *
     * 注意: 方法參數、返回值要與原函數保持一致
     * @return
     */
    public String queryOrderInfo2Fallback(String orderId, Throwable e) {
        return "fallback queryOrderInfo2: " + orderId;
    }



blockHandler = "handleFlowQpsException"用來處理Sentinel 限流/熔斷等錯誤;
fallback = "queryOrderInfo2Fallback"用來處理接口中業務代碼所有異常(如業務代碼異常、sentinel限流熔斷異常等);
注:以上兩種處理方法中方法名、參數都需與受保護的函數保持一致。

測試:

	/**
     * 限流實現方式二: 注解定義資源
     *
     * @param orderId
     * @return
     */
    @RequestMapping("/getOrder2")
    @ResponseBody
    public String queryOrder3(@RequestParam("orderId") String orderId) {
        return orderQueryService.queryOrderInfo2(orderId);
    }

 

測試結果在這里就不貼出來了,結果類似。

下面就使用基於注解的方式實現Sentinel的熔斷降級的demo。


@Component
@Slf4j
public class GoodsQueryService {

    private static final String KEY = "queryGoodsInfo2";

    /**
     * 模擬商品查詢接口
     *
     * @param spuId
     * @return
     */
    @SentinelResource(value = KEY, blockHandler = "blockHandlerMethod", fallback = "queryGoodsInfoFallback")
    public String queryGoodsInfo(String spuId) {

        // 模擬調用服務出現異常
        if ("0".equals(spuId)) {
            throw new RuntimeException();
        }

        return "query goodsinfo success, " + spuId;
    }

    public String blockHandlerMethod(String spuId, BlockException e) {
        log.warn("queryGoodsInfo222 blockHandler", e.toString());
        return "queryGoodsInfo error, blockHandlerMethod res: " + spuId;

    }

    public String queryGoodsInfoFallback(String spuId, Throwable e) {
        log.warn("queryGoodsInfo222 fallback", e.toString());
        return "queryGoodsInfo error, return fallback res: " + spuId;
    }

    @PostConstruct
    public void initDegradeRule() {
        List<DegradeRule> rules = new ArrayList<>();
        DegradeRule rule = new DegradeRule();
        rule.setResource(KEY);
        // 80s內調用接口出現異常次數超過5的時候, 進行熔斷
        rule.setCount(5);
        rule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT);
        rule.setTimeWindow(80);
        rules.add(rule);
        DegradeRuleManager.loadRules(rules);
    }
}


// 測試類

@Controller
@RequestMapping("goods")
public class GoodsController {

    @Autowired
    private GoodsQueryService goodsQueryService;

    @RequestMapping("/queryGoodsInfo")
    @ResponseBody
    public String queryGoodsInfo(@RequestParam("spuId") String spuId) {
        String res = goodsQueryService.queryGoodsInfo(spuId);
        return res;
    }
}

三、控制台的使用
Sentinel 提供一個輕量級的開源控制台,它提供機器發現以及健康情況管理、監控(單機和集群),規則管理和推送的功能。

主要功能有:

查看機器列表以及健康情況;
監控;
規則管理和推送;
鑒權;
1、啟動Sentinel控制台
如何啟動控制台見:啟動控制台

啟動方式:

(1)下載jar包,通過命令方式

在https://github.com/alibaba/Sentinel/releases下載對應版本的dashboard jar包,進入到該jar所在的目錄,然后通過java命令運行該jar包即可:


java -Dserver.port=8080 \
-Dcsp.sentinel.dashboard.server=localhost:8080 \
-jar target/sentinel-dashboard.jar

 

(2)git clone 整個sentinel源碼,進入sentinel-dashboard模塊,執行打包命令:mvn clean package,生成一個可執行的 fat jar包,然后啟動控制台,輸入url:localhost:8080后即可進入主頁面。

2、客戶端接入(Spring Boot項目接入控制台)
啟動了控制台模塊后,控制台頁面都是空的,需要接入客戶端。

(1)首先導入與控制台通信的jar包:

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-transport-simple-http</artifactId>
    <version>${sentinel.version}</version>
</dependency>

 

 

(2)配置 JVM 啟動參數:


-Dproject.name=sentinel-demo -Dcsp.sentinel.dashboard.server=127.0.0.1:8080 -Dcsp.sentinel.api.port=8719

 

啟動應用。

注:在應用中需要有觸發限流、降級的初始化代碼。

本demo中http://localhost:8083/order/getOrder1?orderId=123接口執行多次,會觸發限流操作,這時候再去看控制台:

可以看到,getOrderInfo接口被限流的次數。

4、控制台的使用
如果我們想使用Sentinel的限流和熔斷功能,除了在代碼中可以硬編碼外,也可直接通過控制台進行粗略的對接口進行限流和熔斷。

/**
     * 代碼不加任何限流 熔斷
     * @return
     */
    @RequestMapping("/test")
    @ResponseBody
    @SentinelResource("test")
    public String test() {
        return "test";
    }

 

執行http://localhost:8083/goods/test接口動作后,在頁面上簇點鏈路中即可看到該接口已經被統計到:

點擊流控,對該接口新增一條流控規則,配置如下:

點擊新增后,

再去執行接口http://localhost:8083/goods/test,當QPS超過2時,即可看到接口返回異常,達到流控效果。

當然,控制台流控規則、降級規則的使用還有很多,可以參考官方文檔看看具體如何使用。

PS

在接入控制台過程中,發現控制台一直會報error:

ERROR 5317 --- [pool-2-thread-1] c.a.c.s.dashboard.metric.MetricFetcher   : Failed to fetch metric from <http://10.200.183.30:8721/metric?startTime=1575117735000&endTime=1575117741000&refetch=false> (ConnectionException: Connection refused)

 

錯誤原因是控制台在收集客戶端數據時,從IP地址http://10.200.183.30:8721連接不上;

后發現原來開啟了代理,關閉代理后就正常。

參考:

官方文檔:https://github.com/alibaba/Sentinel/wiki

原文鏈接:https://blog.csdn.net/noaman_wgs/article/details/103328793

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM