SpringCloudAlibaba之Sentinel框架

一、Sentinel 介紹

隨着微服務的流行,服務和服務之間的穩定性變得越來越重要。 https://github.com/alibaba/Sentinel[Sentinel] 以流量爲切入點,從流量控制、熔斷降級、系統負載保護等多個維度保護服務的穩定性。

https://github.com/alibaba/Sentinel[Sentinel] 具有以下特徵:

  • 豐富的應用場景: Sentinel 承接了阿里巴巴近 10 年的雙十一大促流量的核心場景,例如秒殺(即突發流量控制在系統容量可以承受的範圍)、消息削峯填谷、實時熔斷下游不可用應用等。
  • 完備的實時監控: Sentinel 同時提供實時的監控功能。您可以在控制檯中看到接入應用的單臺機器秒級數據,甚至 500 臺以下規模的集羣的彙總運行情況。
  • 廣泛的開源生態: Sentinel 提供開箱即用的與其它開源框架/庫的整合模塊,例如與 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相應的依賴並進行簡單的配置即可快速地接入 Sentinel。
  • 完善的 SPI 擴展點: Sentinel 提供簡單易用、完善的 SPI 擴展點。您可以通過實現擴展點,快速的定製邏輯。例如定製規則管理、適配數據源等。

服務保護的基本概念

服務限流/熔斷

在高併發情況下,客戶端請求達到了一定的極限,也就是我們所設定的閾值,服務就會自動開啓自我保護機制,直接走我們的服務降級fallback方法,給客戶端一個友好提示。

服務降級

在高併發情況下,爲了防止用戶一直等待,給用戶一個友好提示。

服務的雪崩效應

默認情況下,tomcat/jetty服務器只有一個線程池去處理用戶請求。在高併發情況下,如果客戶端的所有請求都堆積在同一個接口上,線程池中的所有線程都用來處理這些請求,就會導致其他接口無法訪問。

服務隔離機制

服務隔離機制分爲兩種:信號量隔離和線程池隔離

信號量隔離:最多只能有一定的閾值的線程數來處理我們的請求,超過閾值就會拒絕請求

線程池隔離:每個服務接口都有獨立的線程池來處理請求,接口之間互不影響,缺點:佔用CPU資源較大

Sentinel和Hytrix區別

在這裏插入圖片描述

二、Sentinel 環境快速搭建

下載對應Sentinel-Dashboard

https://github.com/alibaba/Sentinel/releases/tag/1.7.1 運行即可。

默認賬號密碼:sentinel/sentinel

運行執行命令

java -Dserver.port=8718 -Dcsp.sentinel.dashboard.server=localhost:8718 -Dproject.name=sentinel-dashboard -Dcsp.sentinel.api.port=8719 -jar sentinel-dashboard-1.7.1.jar

三、Sentinel實現API動態限流

maven依賴

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-alibaba-sentinel</artifactId>
    <version>0.2.2.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

1.手動配置管理API限流接口

private static final String GETORDER_KEY = "getOrder";

@RequestMapping("/getOrder")
public String getOrders() {
    Entry entry = null;
    try {
        entry = SphU.entry(GETORDER_KEY);
        // 執行我們服務需要保護的業務邏輯
        return "getOrder接口";
    } catch (Exception e) {
        e.printStackTrace();
        return "該服務接口已經達到上線!";
    } finally {
        // SphU.entry(xxx) 需要與 entry.exit() 成對出現,否則會導致調用鏈記錄異常
        if (entry != null) {
            entry.exit();
        }
    }
}

限流配置放到項目自動加載

@Component
@Slf4j
public class SentinelApplicationRunner implements ApplicationRunner {
    private static final String GETORDER_KEY = "getOrder";

    @Override
    public void run(ApplicationArguments args) throws Exception {
        List<FlowRule> rules = new ArrayList<FlowRule>();
        FlowRule rule1 = new FlowRule();
        rule1.setResource(GETORDER_KEY);
        // QPS控制在2以內
        rule1.setCount(1);
        // QPS限流
        rule1.setGrade(RuleConstant.FLOW_GRADE_QPS);
        rule1.setLimitApp("default");
        rules.add(rule1);
        FlowRuleManager.loadRules(rules);
        log.info(">>>限流服務接口配置加載成功>>>");
    }
}

2.註解形式配置管理Api限流

2.1 基於QPS限流
private static final String GETORDER_KEY = "getOrder";

    /***
     * @SentinelResource 流量規則資源名
     * - blockHandler 限流/熔斷 出現異常 執行的方法
     * - fallback 服務降級執行的方法
     * -QPS=併發數/平均響應時間
     * @return
     */
    @SentinelResource(value = GETORDER_KEY,blockHandler = "getOrderQpsException")
    @RequestMapping("/getOrder")
    public String getOrders() {
        return "getOrder接口";
    }

    /**
     * 被限流後返回的提示
     *
     * @param e
     * @return
     */
    public String getOrderQpsException(BlockException e) {
        e.printStackTrace();
        return "該接口已經被限流啦!";
    }

application.yml

server:
  port: 8080
spring:
  cloud:
    sentinel:
      transport:
        port: 8719
        dashboard: 192.168.75.137:8718
      eager: true
  application:
    name: Sentinel-02

Sentinel控制檯中添加限流規則

在這裏插入圖片描述

2.2 基於併發數限流
    /***
     * 基於併發數量處理限流
     * 併發數 = QPS*平均響應時間
     * 每次最多隻會有一個線程處理該業務邏輯,超出該閾值的情況下,直接拒絕訪問。
     * @return
     */
    @SentinelResource(value = "getOrderThrad", blockHandler = "getOrderQpsException")
    @RequestMapping("/getOrderThrad")
    public String getgetOrderThread(){
        System.out.println(Thread.currentThread().getName());
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "getOrderThrad";
    }

Sentinel控制檯中添加限流規則

在這裏插入圖片描述

如果要在您的項目中引入 Sentinel,使用 group ID 爲 com.alibaba.cloud 和 artifact ID 爲 spring-cloud-starter-alibaba-sentinel 的 starter。

<dependency>
    <groupId>com.alibaba.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 還提供了其它額外的屬性如 blockHandlerblockHandlerClassfallback 用於表示限流或降級的操作,更多內容可以參考 https://github.com/alibaba/Sentinel/wiki/%E6%B3%A8%E8%A7%A3%E6%94%AF%E6%8C%81[Sentinel註解支持]。

以上例子都是在 WebServlet 環境下使用的,Sentinel 目前已經支持 WebFlux,需要配合 spring-boot-starter-webflux 依賴觸發 sentinel-starter 中 WebFlux 相關的自動化配置。

@SpringBootApplication
public class Application {

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

}

@RestController
public class TestController {

    @GetMapping("/mono")
    @SentinelResource("hello")
    public Mono<String> mono() {
	return Mono.just("simple string")
			.transform(new SentinelReactorTransformer<>("otherResourceName"));
    }

}

3. 基於熱點參數手動實現限流

添加maven依賴:

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-parameter-flow-control</artifactId>
    <version>1.6.3</version>
    <scope>compile</scope>
</dependency>

將限流規則,手動放入到項目啓動自動加載:

@Component
public class SentinelApplicationRunner implements ApplicationRunner {
    private static final String GETORDER_KEY = "getOrder";

    @Override
    public void run(ApplicationArguments args) throws Exception {
        // 定義熱點限流的規則,對第一個參數設置 qps 限流模式,閾值爲5
        ParamFlowRule rule = new ParamFlowRule(GETORDER_KEY)
                //熱點參數的索引
                .setParamIdx(0)
                //限流模式
                .setGrade(RuleConstant.FLOW_GRADE_QPS)
                //閾值
                .setCount(2);
        ParamFlowRuleManager.loadRules(Collections.singletonList(rule));
    }
}

controller層:

@RestController
public class IndexController {
    private static final String GETORDER_KEY = "getOrder";

    @GetMapping("getOrder")
    public String getOrder(int id){
        Entry entry = null;
        try {
            //參數1:資源名稱,參數三:計數,參數四:限流參數
            entry = SphU.entry(GETORDER_KEY, EntryType.IN, 1, id);
            // Your logic here.
        } catch (BlockException ex) {
            return "該用戶服務已經限流,id="+id;
        }finally {
            if (entry != null) {
                entry.exit();
            }
        }
        return GETORDER_KEY+",id="+id;
    }
}

4. 基於熱點參數控制檯實現限流

java代碼:

@RestController
public class IndexController {
    private static final String GETORDER_KEY = "getOrder";

    @GetMapping("getOrder")
    @SentinelResource(GETORDER_KEY)
    public String getOrder(int id) {
        return GETORDER_KEY + ".id=" + id;
    }
}

application.yml

server:
  port: 8080
spring:
  cloud:
    sentinel:
      transport:
        port: 8719
        dashboard: 192.168.75.137:8718
      eager: true

Sentinel控制檯中添加熱點參數限流規則:
在這裏插入圖片描述

四、Sentinel實現API熔斷降級

4.1 基於平均響應時間

java代碼:

    /***
     * 基於平均響應時間
     * @return
     */
    @SentinelResource(value = "getERFallBack",fallback = "getException")
    @GetMapping("getERFallBack")
    public String getERFallBack(){
        try {
            Thread.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "getERFallBack";
    }

    /***
     * 基於平均響應時間 的降級方法
     * @return
     */
    public String getException(){
        return "服務降級啦,當前服務器請求次數過多,請稍後重試!";
    }

Sentinel控制檯:

每秒平均響應時間超過閾值200ms,走服務降級,並且5秒內無法訪問服務,繼續走服務降級方法。

在這裏插入圖片描述

4.2 基於異常比例

java代碼:

    /***
     * 基於異常比例
     * @param i
     * @return
     */
    @SentinelResource(value = "getErroPer",fallback = "getException")
    @GetMapping("/getErroPer")
    public String getErroPer(int i){
        int j=1/i;
        return "getErroPer";
    }

    /***
     * 參數必須與被降級的方法中的參數一致,才能實現降級
     * 異常比例和異常次數 的降級方法
     * @param i
     * @return
     */
    public String getException(int i){
        return "服務降級啦,請稍後重試!";
    }

Sentinel控制檯:

在這裏插入圖片描述

4.3 基於異常次數

java代碼:

    /***
     * 基於異常次數
     * @param i
     * @return
     */
    @SentinelResource(value = "getErroCout",fallback = "getException")
    @GetMapping("/getErroCout")
    public String getErroCout(int i){
        int j=1/i;
        return "getErroCout";
    }

    /***
     * 參數必須與被降級的方法中的參數一致,才能實現降級
     * 異常比例和異常次數 的降級方法
     * @param i
     * @return
     */
    public String getException(int i){
        return "服務降級啦,請稍後重試!";
    }

Sentinel控制檯:

在這裏插入圖片描述

五、RestTemplate 支持

Spring Cloud Alibaba Sentinel 支持對 RestTemplate 的服務調用使用 Sentinel 進行保護,在構造 RestTemplate bean的時候需要加上 @SentinelRestTemplate 註解。

@Bean
@SentinelRestTemplate(blockHandler = "handleException", blockHandlerClass = ExceptionUtil.class)
public RestTemplate restTemplate() {
    return new RestTemplate();
}

@SentinelRestTemplate 註解的屬性支持限流(blockHandler, blockHandlerClass)和降級(fallback, fallbackClass)的處理。

其中 blockHandlerfallback 屬性對應的方法必須是對應 blockHandlerClassfallbackClass 屬性中的靜態方法。

該方法的參數跟返回值跟 org.springframework.http.client.ClientHttpRequestInterceptor#interceptor 方法一致,其中參數多出了一個 BlockException 參數用於獲取 Sentinel 捕獲的異常。

比如上述 @SentinelRestTemplate 註解中 ExceptionUtilhandleException 屬性對應的方法聲明如下:

public class ExceptionUtil {
    public static ClientHttpResponse handleException(HttpRequest request, byte[] body, ClientHttpRequestExecution execution, BlockException exception) {
        ...
    }
}

NOTE: 應用啓動的時候會檢查 @SentinelRestTemplate 註解對應的限流或降級方法是否存在,如不存在會拋出異常

@SentinelRestTemplate 註解的限流(blockHandler, blockHandlerClass)和降級(fallback, fallbackClass)屬性不強制填寫。

當使用 RestTemplate 調用被 Sentinel 熔斷後,會返回 RestTemplate request block by sentinel 信息,或者也可以編寫對應的方法自行處理返回信息。這裏提供了 SentinelClientHttpResponse 用於構造返回信息。

Sentinel RestTemplate 限流的資源規則提供兩種粒度:

  • httpmethod:schema://host:port/path:協議、主機、端口和路徑

  • httpmethod:schema://host:port:協議、主機和端口

NOTE: 以 https://www.taobao.com/test 這個 url 並使用 GET 方法爲例。對應的資源名有兩種粒度,分別是 GET:https://www.taobao.com 以及 GET:https://www.taobao.com/test

六、動態數據源支持(數據持久化)

Sentinel中提供四種數據源實現數據持久化:File、Nacos、Zookeeper、Apolle

配置案例:

spring.cloud.sentinel.datasource.ds1.file.file=classpath: degraderule.json
spring.cloud.sentinel.datasource.ds1.file.rule-type=flow

#spring.cloud.sentinel.datasource.ds1.file.file=classpath: flowrule.json
#spring.cloud.sentinel.datasource.ds1.file.data-type=custom
#spring.cloud.sentinel.datasource.ds1.file.converter-class=org.springframework.cloud.alibaba.cloud.examples.JsonFlowRuleListConverter
#spring.cloud.sentinel.datasource.ds1.file.rule-type=flow

spring.cloud.sentinel.datasource.ds2.nacos.server-addr=localhost:8848
spring.cloud.sentinel.datasource.ds2.nacos.data-id=sentinel
spring.cloud.sentinel.datasource.ds2.nacos.group-id=DEFAULT_GROUP
spring.cloud.sentinel.datasource.ds2.nacos.data-type=json
spring.cloud.sentinel.datasource.ds2.nacos.rule-type=degrade

spring.cloud.sentinel.datasource.ds3.zk.path = /Sentinel-Demo/SYSTEM-CODE-DEMO-FLOW
spring.cloud.sentinel.datasource.ds3.zk.server-addr = localhost:2181
spring.cloud.sentinel.datasource.ds3.zk.rule-type=authority

spring.cloud.sentinel.datasource.ds4.apollo.namespace-name = application
spring.cloud.sentinel.datasource.ds4.apollo.flow-rules-key = sentinel
spring.cloud.sentinel.datasource.ds4.apollo.default-flow-rule-value = test
spring.cloud.sentinel.datasource.ds4.apollo.rule-type=param-flow

每種數據源都有兩個共同的配置項: data-typeconverter-class 以及 rule-type

data-type :表示數據類型,Sentinel 默認提供兩種內置的值,分別是 jsonxml (默認是json)。 若不想使用這兩種,可以添加 custom 自定義數據類型,再配置 converter-class 配置項,該配置項需要寫類的全路徑名(比如 spring.cloud.sentinel.datasource.ds1.file.converter-class=org.springframework.cloud.alibaba.cloud.examples.JsonFlowRuleListConverter)。

rule-type :表示數據規則,(flowdegradeauthoritysystem, param-flow, gw-flow, gw-api-group)。

6.1 基於Nacos實現數據持久化

添加maven依賴:

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
    <version>1.5.2</version>
</dependency>

application.yml

server:
  port: 8080
spring:
  cloud:
    sentinel:
      transport:
        port: 8719
        dashboard: 192.168.75.137:8718
      eager: true
      datasource:
        ds:
          nacos:
            ### nacos連接地址
            server-addr: 192.168.75.137:8848
            ## nacos連接的分組
            group-id: DEFAULT_GROUP
            ###路由存儲規則
            rule-type: flow
            ### 讀取配置文件的 data-id
            data-id: Sentinel-03
            ###  讀取培訓文件類型爲json
            data-type: json
      log:
        dir: D:\Code\study\Java_Learning\01 Sentinel\03 基於Nacos實現數據持久化\logs\
  application:
    name: Sentinel-03

java代碼:

    @SentinelResource(value = "getNacosInfo",blockHandler = "getQpsException")
    @GetMapping("/getNacosInfo")
    public String getNacosInfo(){
        return "getNacosInfo";
    }

    public String getQpsException(){
        return "該接口已被限流,請稍後再試";
    }

Nacos控制檯:

在這裏插入圖片描述

resource:資源名,即限流規則的作用對象

limitApp:流控針對的調用來源,若爲 default 則不區分調用來源

grade:限流閾值類型(QPS 或併發線程數);0代表根據併發數量來限流,1代表根據QPS來進行流量控制

count:限流閾值

strategy:調用關係限流策略

七、網關限流

https://github.com/alibaba/Sentinel/wiki/%E7%BD%91%E5%85%B3%E9%99%90%E6%B5%81 [參考 Sentinel 網關限流]

注:github源碼

發佈了104 篇原創文章 · 獲贊 35 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章