SpringCloud-Alibaba-Sentinel(1)初探

專注後端發表於2019-07-19

Sentinel 是什麼?

Sentinel 具有以下特徵:

豐富的應用場景:Sentinel 承接了阿里巴巴近 10 年的雙十一大促流量的核心場景,例如秒殺(即突發流量控制在系統容量可以承受的範圍)、訊息削峰填谷、叢集流量控制、實時熔斷下游不可用應用等。

完備的實時監控:Sentinel 同時提供實時的監控功能。您可以在控制檯中看到接入應用的單臺機器秒級資料,甚至 500 臺以下規模的叢集的彙總執行情況。

廣泛的開源生態:Sentinel 提供開箱即用的與其它開源框架/庫的整合模組,例如與 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相應的依賴並進行簡單的配置即可快速地接入 Sentinel。

完善的 SPI 擴充套件點:Sentinel 提供簡單易用、完善的 SPI 擴充套件介面。您可以通過實現擴充套件介面來快速地定製邏輯。例如定製規則管理、適配動態資料來源等。

 

Sentinel 的主要特性:

Sentinel 的開源生態:

我們大概敘述後開始整合我們的分散式專案,我這裡裡為一個簡單分散式專案,註冊中心為Nacos,一個服務提供者(user-center)和一個服務消費者(content-center)

 

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.Collectors;

@Slf4j
@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class ShareService {
    private final ShareMapper shareMapper;
    private final RestTemplate restTemplate;

    public ShareDTO findById(Integer id) {
        // 獲取分享詳情
        Share share = this.shareMapper.selectByPrimaryKey(id);
        // 釋出人id
        Integer userId = share.getUserId();

        UserDTO userDTO = this.restTemplate.getForObject(
            "http://user-center/users/{userId}",
            UserDTO.class, userId
        );

        ShareDTO shareDTO = new ShareDTO();
        // 訊息的裝配
        BeanUtils.copyProperties(share, shareDTO);
//        shareDTO.setWxNickname(userDTO.getWxNickname());
        return shareDTO;
    }

  
}

可以看到我的content-center呼叫user-center/users服務

import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/shares")
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class ShareConroller {
    private final ShareService shareService;

    @GetMapping("/{id}")
    public ShareDTO findById(@PathVariable Integer id) {
        return this.shareService.findById(id);
    }
}

我們現在只啟動content-center不啟動user服務,可想而知肯定報錯,我們來看

沒錯500,找不到user-center服務,我們現在整合Sentinel,首先加入兩個依賴

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

actuator配合使用,可通過http://localhost:8010/actuator/sentinel端點進行檢視

 

{
    "blockPage":null,
    "appName":"content-center",
    "consoleServer":"localhost:8333",
    "coldFactor":"3",
    "rules":{
        "systemRules":[
​
        ],
        "authorityRule":[
​
        ],
        "paramFlowRule":[
​
        ],
        "flowRules":[
​
        ],
        "degradeRules":[
​
        ]
    },
    "metricsFileCharset":"UTF-8",
    "filter":{
        "order":-2147483648,
        "urlPatterns":[
            "/*"
        ],
        "enabled":true
    },
    "totalMetricsFileCount":6,
    "datasource":{
​
    },
    "clientIp":"192.168.2.29",
    "clientPort":"8719",
    "logUsePid":false,
    "metricsFileSize":52428800,
    "logDir":"/Users/zhangguowei/logs/csp/",
    "heartbeatIntervalMs":10000
}

我們暫時先不要管這些是什麼我們後面再詳細闡述,從JSON的形式看不太方便,Sentinel為我們整合有UI介面

整合Sentinel視覺化介面

下載 sentinel的jar包然後直接啟動https://github.com/alibaba/Sentinel/releases

啟動的時候可以自定義埠號

賬號密碼預設為sentinel,在專案的yml中加入

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/content_center
    hikari:
      username: ***
      password: ******
      driver-class-name: com.mysql.cj.jdbc.Driver
  cloud:
    sentinel:
      transport:
        # 指向sentinel UI介面
        dashboard: localhost:8333
    nacos:
      discovery:
        # 指定nacos server的地址
        server-addr: localhost:8848
  application:
    # 服務名稱儘量用-,不要用_,不要用特殊字元
    name: content-center
server:
  port: 8010
management:
  endpoints:
    web:
      exposure:
        include: "*"

這個時候我們繼續剛剛的500再執行幾次會看到

相對於原來的Hystrix 的UI介面 Sentinel更人性化

他支援QPS的檢視,請求時間等等 

那麼我們首先來探討流控規則,我們現在把user服務也啟動,我們點選簇點鏈路

  

可以看到content曾經訪問過的路徑,我們點選流控按鈕,就可以為這路徑設定流控規則

我們可以看到資源名預設為我們請求的路徑,其實也不一定是路徑,他只是做唯一標識,

針對來源:Sentinel可以針對呼叫者進行限流控制,比如兩個服務呼叫content服務,我可以把針對來源寫為A然後設定QPS的閾值為200B300QPS,也就是我當前訪問的API介面對不同的服務有不同的限流規則,那麼就可以用針對來源來設定,我們暫時用default,標識不區分來源,後面詳細說如何區分來源。

 

閾值型別有兩個選項,分別是QPS和執行緒數,如果選擇QPS然後設定閾值,就是當這個API達到這個QPS閾值時會進行限流,同理也可以使用執行緒數限流,當達到一定的執行緒數的時候進行限流,是否叢集我們在後面會詳細探討。

流控模式,sentinel提供了三種流控模式,我們先來看直接我們設定直接並儲存

可以看到訪問一次直接再次訪問就直接限流了。那關聯是什麼意思,也就是說當關聯的資源達到閾值,就限流自己,我們來嘗試,剛剛上面所說我們可以用http://localhost:8010/actuator/sentinel 來檢視資訊,我們用/shares/1關聯actuator/sentinel

 

 

它表示actuator/sentinel這個端點達到這個閾值那麼就限流/shares/1這個路徑,

我們來寫段測試程式碼

import org.springframework.web.client.RestTemplate;

public class SentinelTest {

    public static void main(String[] args) {
        RestTemplate restTemplate = new RestTemplate();
        for (int i = 0; i <10000 ; i++) {
            String forObject = restTemplate.getForObject("http://localhost:8010/actuator/sentinel", String.class);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        
    }
}

理論上這樣呼叫的話QPS肯定大於1,那麼此時/shares/1會被限流

此時/shares/1會一直處於限流狀態,他適用於什麼場景:比如一個請求查詢shares一個來修改shares處於高併發狀態,查詢過快的話會影響修改的效能,修改快的話會影響查詢效能,所以這個時候可以根據業務場景,是希望優先讀還是優先改,如果我們希望優先改,我們可以把關聯的資源寫成修改的API,資源名設定為查詢API,這樣修改超過閾值的話查詢就會被限流。總的來說關聯就是保護關聯資源的設計。

我們繼續來說第三個模式

點選鏈路會變成入口資源,鏈路:只記錄指定鏈路上的流量,指定資源從入口資源進來的流量達到閾值就會限流,我們繼續寫程式碼來測試

這裡用到@SentinelResource註解,後續我們會說到

我們訪問後點選簇點鏈路

 

會發現有testatestb

以及common

我們在common上面點選流控  

 

我們把入口寫testa

 

第一次請求正常請求

請求多次後直接500被限流而testb無論怎麼請求都會正常請求,

這其實就是細粒度的針對來源,我們上面所說的針對來源是微服務級別而鏈路是API級別的。那現在我們三種流控模式都嘗試完畢,我們恢復成直接狀態,最後我們來探討流控效果

 

快速失敗很好理解,就是直接拋異常,在測試當中我們也看到了,他的原始碼在com.alibaba.csp.sentinel.slots.block.flow.controller.DefaultController 目錄中

第二種叫Warm Up 

什麼意思呢?就是說sentinel也有個預設值為3,從閾值/3,經過預熱時長,才到達設定的QPS閾值。也就是說,我把閾值設定為100我把預熱時長設定為10

 

那麼他就會用1003作為最初的閾值,然後經過10秒之後才去達到這個100限流。比如有一個秒殺微服務,平常的併發並不是很高,但是在某一個瞬間,某一場秒殺活動開始,在開始的瞬間流量會激增,如果不做任何處理,可能服務一下就宕了,所以Sentinel設計Warm Up這種模式,可以讓允許通過的流量緩慢的增加,在經過10秒之後才達到設定的閾值,這樣就會更好的保護我們的微服務

相關原始碼在:com.alibaba.csp.sentinel.slots.block.flow.controller.WarmUpController

我們來看最後一種效果

 

 

這個是排隊等待,這個也很好理解就是 勻速排隊,讓請求以均勻的速度通過,但是閾值的型別必須設定為QPS,否則都無效了

也就說比如我閾值10060只允許閾值為100,其他的請求在後面排隊直到設定的超時時間,如果過了超時時間求丟棄這次請求,這裡我就不再做實驗,他比較適用於突發流量場景,比如你的系統突然來了好多流量,一會就比較少,你希望應用在空閒的時候處理請求而不直接拒絕就可以使用排隊等待。這個模式的原始碼路徑為:

com.alibaba.csp.sentinel.slots.block.flow.controller.RateLimiterController

暫時先說到這裡 sentinel提供很多非常靈活,而且還有強大的限流規則。