Sentinel入門

前言

Sentinel 是什么巴元?

隨著微服務(wù)的流行,服務(wù)和服務(wù)之間的穩(wěn)定性變得越來越重要陋率。Sentinel 以流量為切入點(diǎn)球化,從流量控制、熔斷降級(jí)瓦糟、系統(tǒng)負(fù)載保護(hù)等多個(gè)維度保護(hù)服務(wù)的穩(wěn)定性筒愚。

Sentinel 的歷史

  • 2012 年,Sentinel 誕生菩浙,主要功能為入口流量控制巢掺。
  • 2013-2017 年,Sentinel 在阿里巴巴集團(tuán)內(nèi)部迅速發(fā)展劲蜻,成為基礎(chǔ)技術(shù)模塊陆淀,覆蓋了所有的核心場(chǎng)景。Sentinel 也因此積累了大量的流量歸整場(chǎng)景以及生產(chǎn)實(shí)踐先嬉。
  • 2018 年轧苫,Sentinel 開源,并持續(xù)演進(jìn)疫蔓。
  • 2019 年含懊,Sentinel 朝著多語言擴(kuò)展的方向不斷探索,推出 C++ 原生版本鳄袍,同時(shí)針對(duì) Service Mesh 場(chǎng)景也推出了 Envoy 集群流量控制支持,以解決 Service Mesh 架構(gòu)下多語言限流的問題拗小。
  • 2020 年哀九,推出 Sentinel Go 版本,繼續(xù)朝著云原生方向演進(jìn)阅束。

Sentinel 特征

  • 豐富的應(yīng)用場(chǎng)景:Sentinel 承接了阿里巴巴近 10 年的雙十一大促流量的核心場(chǎng)景茄唐,例如秒殺(即突發(fā)流量控制在系統(tǒng)容量可以承受的范圍)蝇更、消息削峰填谷呼盆、集群流量控制、實(shí)時(shí)熔斷下游不可用應(yīng)用等厨幻。
  • 完備的實(shí)時(shí)監(jiān)控:Sentinel 同時(shí)提供實(shí)時(shí)的監(jiān)控功能。您可以在控制臺(tái)中看到接入應(yīng)用的單臺(tái)機(jī)器秒級(jí)數(shù)據(jù)腿时,甚至 500 臺(tái)以下規(guī)模的集群的匯總運(yùn)行情況况脆。
  • 廣泛的開源生態(tài):Sentinel 提供開箱即用的與其它開源框架/庫的整合模塊,例如與 Spring Cloud批糟、Dubbo格了、gRPC 的整合。您只需要引入相應(yīng)的依賴并進(jìn)行簡單的配置即可快速地接入 Sentinel徽鼎。
  • 完善的 SPI 擴(kuò)展點(diǎn):Sentinel 提供簡單易用盛末、完善的 SPI 擴(kuò)展接口。您可以通過實(shí)現(xiàn)擴(kuò)展接口來快速地定制邏輯纬傲。例如定制規(guī)則管理满败、適配動(dòng)態(tài)數(shù)據(jù)源等。

Sentinel 的主要特性

圖片.png

Sentinel 的開源生態(tài)

圖片.png

Sentinel 分為兩個(gè)部分

  • 核心庫(Java 客戶端)不依賴任何框架/庫叹括,能夠運(yùn)行于所有 Java 運(yùn)行時(shí)環(huán)境算墨,同時(shí)對(duì) Dubbo / Spring Cloud 等框架也有較好的支持。
  • 控制臺(tái)(Dashboard)基于 Spring Boot 開發(fā)汁雷,打包后可以直接運(yùn)行净嘀,不需要額外的 Tomcat 等應(yīng)用容器。

快速開始

1.添加pom依賴

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

注意: 從 Sentinel 1.5.0 開始僅支持 JDK 1.7 或者以上版本侠讯。Sentinel 1.5.0 之前的版本最低支持 JDK 1.6挖藏。

2.定義資源

接下來,我們把需要控制流量的代碼用 Sentinel API SphU.entry("HelloWorld") 和 entry.exit() 包圍起來即可厢漩。

public static void main(String[] args) {
    // 配置規(guī)則.
    initFlowRules();
    while (true) {
        Entry entry = null;
        try {
        entry = SphU.entry("HelloWorld");
            /*您的業(yè)務(wù)邏輯 - 開始*/
            System.out.println("hello world");
            /*您的業(yè)務(wù)邏輯 - 結(jié)束*/
    } catch (BlockException e1) {
            /*流控邏輯處理 - 開始*/
        System.out.println("block!");
            /*流控邏輯處理 - 結(jié)束*/
    } finally {
       if (entry != null) {
           entry.exit();
       }
    }
    }
}

3.定義規(guī)則

接下來膜眠,通過規(guī)則來指定允許該資源通過的請(qǐng)求次數(shù),例如下面的代碼定義了資源 HelloWorld 每秒最多只能通過 20 個(gè)請(qǐng)求溜嗜。

private static void initFlowRules(){
    List<FlowRule> rules = new ArrayList<>();
    FlowRule rule = new FlowRule();
    rule.setResource("HelloWorld");
    rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
    // Set limit QPS to 20.
    rule.setCount(20);
    rules.add(rule);
    FlowRuleManager.loadRules(rules);
}

注解支持

Sentinel 提供了 @SentinelResource 注解用于定義資源宵膨,并提供了 AspectJ 的擴(kuò)展用于自動(dòng)定義資源、處理 BlockException 等炸宵。使用 Sentinel Annotation AspectJ Extension 的時(shí)候需要引入以下依賴:

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

將 SentinelResourceAspect 注冊(cè)為一個(gè) Spring Bean

@Configuration
public class SentinelAspectConfiguration {

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

示例代碼

public class TestService {

    // 對(duì)應(yīng)的 `handleException` 函數(shù)需要位于 `ExceptionUtil` 類中辟躏,并且必須為 static 函數(shù).
    @SentinelResource(value = "test", blockHandler = "handleException", blockHandlerClass = {ExceptionUtil.class})
    public void test() {
        System.out.println("Test");
    }

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

    // Block 異常處理函數(shù)末秃,參數(shù)最后多一個(gè) BlockException蛔溃,其余與原函數(shù)一致.
    public String exceptionHandler(long s, BlockException ex) {
        // Do some log here.
        ex.printStackTrace();
        return "Oops, error occurred at " + s;
    }
}

代碼實(shí)戰(zhàn)

新建一個(gè)SpringBoot的項(xiàng)目

1.pom依賴

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>com.example.sentinel</groupId>
    <artifactId>sentinel-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>sentinel-demo</name>
    <description>sentinel demo</description>

    <properties>
        <java.version>1.8</java.version>
        <spring.boot.version>2.2.1.RELEASE</spring.boot.version>
        <sentinel.version>1.7.0</sentinel.version>
    </properties>

    <dependencies>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <version>${spring.boot.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>${spring.boot.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
            <version>${spring.boot.version}</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

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

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

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

2.Controller

package com.example.sentinel.sentineldemo.controller;

import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.example.sentinel.sentineldemo.service.TestSentinelService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

/**
 * @Author: Monday
 * @Date: 2020/4/1 0001 上午 11:44
 * @Description:
 */
@Controller
@RequestMapping("test")
public class TestSentinelController {

    private static final String KEY = "queryOne";

    @Autowired
    private TestSentinelService testSentinelService;

    /**
     * 代碼不加任何限流 熔斷
     *
     * @return
     */
    @RequestMapping("/getValue_0")
    @ResponseBody
    @SentinelResource("queryZero")
    public String getValue_0(@RequestParam("key") String key) {
        return testSentinelService.getValue_0(key);
    }


    /**
     * 限流實(shí)現(xiàn)方式一: 拋出異常的方式定義資源
     *
     * @param key
     * @return
     */
    @RequestMapping("/getValue_1")
    @ResponseBody
    public String getValue_1(@RequestParam("key") String key) {
        Entry entry = null;
        // 資源名
        String resourceName = KEY;
        try {
            // entry可以理解成入口登記
            entry = SphU.entry(resourceName);
            // 被保護(hù)的邏輯, 這里為查詢接口
            return testSentinelService.getValue_1(key);
        } catch (BlockException blockException) {
            // 接口被限流的時(shí)候, 會(huì)進(jìn)入到這里
            return "接口限流, 返回空";
        } finally {
            // SphU.entry(xxx) 需要與 entry.exit() 成對(duì)出現(xiàn),否則會(huì)導(dǎo)致調(diào)用鏈記錄異常
            if (entry != null) {
                entry.exit();
            }
        }
    }

    /**
     * 限流實(shí)現(xiàn)方式二: 注解定義資源
     *
     * @param key
     * @return
     */
    @RequestMapping("/getValue_2")
    @ResponseBody
    public String getValue_2(@RequestParam("key") String key) {
        String res = testSentinelService.getValue_2(key);
        return res;
    }

}

3.Service

package com.example.sentinel.sentineldemo.service;

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.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

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

/**
 * @Author: Monday
 * @Date: 2020/4/1 0001 上午 11:45
 * @Description: 商品查詢接口
 */
@Component
@Slf4j
public class TestSentinelService {

    private static final String KEY = "queryTwo";

    /**
     * 代碼不加任何限流 熔斷
     *
     * @param key
     * @return
     */
    public String getValue_0(String key) {
        System.out.println("獲取Value:" + key);
        return "return value :" + key;
    }


    /**
     * 拋出異常的方式定義資源
     *
     * @param key
     * @return
     */
    public String getValue_1(String key) {
        System.out.println("獲取Value:" + key);
        return "return value :" + key;
    }

    /**
     * 注解定義資源
     *
     * @param key
     * @return
     */
    @SentinelResource(value = KEY, blockHandler = "blockHandlerMethod", fallback = "queryFallback")
    public String getValue_2(String key) {
        // 模擬調(diào)用服務(wù)出現(xiàn)異常
        if ("0".equals(key)) {
            throw new RuntimeException();
        }
        return "query value success, " + key;
    }

    public String blockHandlerMethod(String key, BlockException e) {
        return "queryValue error, blockHandlerMethod res: " + key;
    }

    public String queryFallback(String key, Throwable e) {
        return "queryValue error, return fallback res: " + key;
    }

    /**
     * 初始化限流配置
     */
    @PostConstruct
    public void initDegradeRule() {
        List<FlowRule> rules = new ArrayList<FlowRule>();
        FlowRule rule1 = new FlowRule();
        rule1.setResource(KEY);
        // QPS控制在2以內(nèi)
        rule1.setCount(2);
        // QPS限流
        rule1.setGrade(RuleConstant.FLOW_GRADE_QPS);
        rule1.setLimitApp("default");
        rules.add(rule1);
        FlowRuleManager.loadRules(rules);
    }
}

4.控制臺(tái)

4.1下載

release 頁面 下載截止目前為止最新版本的控制臺(tái) jar 包

圖片.png

注意:
啟動(dòng) Sentinel 控制臺(tái)需要 JDK 版本為 1.8 及以上版本
從 Sentinel 1.6.0 起,Sentinel 控制臺(tái)引入基本的 登錄 功能雁比,默認(rèn)用戶名和密碼都是 sentinel

用戶可以通過如下參數(shù)進(jìn)行配置

  • -Dsentinel.dashboard.auth.username=sentinel 用于指定控制臺(tái)的登錄用戶名為 sentinel
  • -Dsentinel.dashboard.auth.password=123456 用于指定控制臺(tái)的登錄密碼為 123456偎捎;如果省略這兩個(gè)參數(shù)茴她,默認(rèn)用戶和密碼均為 sentinel
  • -Dserver.servlet.session.timeout=7200 用于指定 Spring Boot 服務(wù)端 session 的過期時(shí)間丈牢,如 7200 表示 7200 秒瞄沙;60m 表示 60 分鐘泛粹,默認(rèn)為 30 分鐘

4.2啟動(dòng)

java -jar sentinel-dashboard-1.7.1.jar

訪問http://localhost:8080

圖片.png

4.3登錄

圖片.png

可以看到當(dāng)前控制臺(tái)中沒有任何的應(yīng)用,因?yàn)檫€沒有應(yīng)用接入伪货。

5.客戶端接入

啟動(dòng)了控制臺(tái)模塊后碱呼,控制臺(tái)頁面都是空的愚臀,需要接入客戶端。

5.1導(dǎo)入與控制臺(tái)通信的jar包

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

5.2 配置應(yīng)用啟動(dòng)參數(shù)

引入了依賴之后馋袜,接著就是在我們的應(yīng)用中配置 JVM 啟動(dòng)參數(shù)欣鳖,如下所示:

-Dproject.name=xxx -Dcsp.sentinel.dashboard.server=consoleIp:port

其中的consoleIp和port對(duì)應(yīng)的就是我們部署的 sentinel dashboard 的ip和port泽台,我這里對(duì)應(yīng)的是 127.0.0.1 和 8080怀酷,按照實(shí)際情況來配置 dashboard 的ip和port就好了蜕依,如下圖所示:


圖片.png

5.3 啟動(dòng)應(yīng)用

啟動(dòng)上邊的springboot項(xiàng)目

5.4 測(cè)試效果

本demo中http://localhost:8083/test/getValue_2?key=kobe接口執(zhí)行多次笔横,會(huì)觸發(fā)限流操作,這時(shí)候再去看控制臺(tái):

圖片.png

圖片.png
圖片.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市晚碾,隨后出現(xiàn)的幾起案子格嘁,更是在濱河造成了極大的恐慌糕簿,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,599評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蜂嗽,死亡現(xiàn)場(chǎng)離奇詭異植旧,居然都是意外死亡离唐,警方通過查閱死者的電腦和手機(jī)侯繁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,629評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門贮竟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來技健,“玉大人惰拱,你說我怎么就攤上這事偿短〗荡” “怎么了勾怒?”我有些...
    開封第一講書人閱讀 158,084評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵段只,是天一觀的道長赞枕。 經(jīng)常有香客問我鹦赎,道長古话,這世上最難降的妖魔是什么陪踩? 我笑而不...
    開封第一講書人閱讀 56,708評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮姥饰,結(jié)果婚禮上列粪,老公的妹妹穿的比我還像新娘岂座。我一直安慰自己,他們只是感情好钾恢,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,813評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著疹瘦,像睡著了一般拱礁。 火紅的嫁衣襯著肌膚如雪呢灶。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,021評(píng)論 1 291
  • 那天缨睡,我揣著相機(jī)與錄音细诸,去河邊找鬼震贵。 笑死猩系,一個(gè)胖子當(dāng)著我的面吹牛寇甸,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播博敬,決...
    沈念sama閱讀 39,120評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼收恢,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼伦意!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起离钝,我...
    開封第一講書人閱讀 37,866評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤褪储,失蹤者是張志新(化名)和其女友劉穎卵渴,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鲤竹,經(jīng)...
    沈念sama閱讀 44,308評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡浪读,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,633評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片碘橘。...
    茶點(diǎn)故事閱讀 38,768評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡互订,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出痘拆,到底是詐尸還是另有隱情仰禽,我是刑警寧澤,帶...
    沈念sama閱讀 34,461評(píng)論 4 333
  • 正文 年R本政府宣布识颊,位于F島的核電站月杉,受9級(jí)特大地震影響腌歉,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜阁危,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,094評(píng)論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦俗批、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,850評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春弃舒,著一層夾襖步出監(jiān)牢的瞬間削锰,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,082評(píng)論 1 267
  • 我被黑心中介騙來泰國打工拗慨, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,571評(píng)論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,666評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容