天天看點

SpringCloud微服務元件:Sentinel限流熔斷

作者:二哥學Java

前言

什麼是雪崩問題?

微服務之間互相調用,因為調用鍊中的一個服務故障,引起整個鍊路都無法通路的情況。

解決雪崩問題的常見方式有四種:

  • 逾時處理:設定逾時時間,請求超過一定時間沒有響應就傳回錯誤資訊,不會無休止等待
  • 艙壁模式:限定每個業務能使用的線程數,避免耗盡整個tomcat的資源,是以也叫線程隔離。
  • 熔斷降級:由斷路器統計業務執行的異常比例,如果超出門檻值則會熔斷該業務,攔截通路該業務的一切請求。
  • 流量控制:限制業務通路的QPS,避免服務因流量的突增而故障。
前三種是一種已經出現後的應對措施可以避免因服務故障引起的雪崩問題;後一種是一個預防方案,可以避免因瞬間高并發流量而導緻服務故障

與Hystrix對比

SpringCloud微服務元件:Sentinel限流熔斷

簇點鍊路:就是項目内的調用鍊路,鍊路中被監控的每個接口就是一個資源。預設情況下sentinel會監控SpringMVC的每一個端點(Endpoint),是以SpringMVC的每一個端點(Endpoint)就是調用鍊路中的一個資源。

流控、熔斷等都是針對簇點鍊路中的資源來設定的,是以我們可以點選對應資源後面的按鈕來設定規則:

SpringCloud微服務元件:Sentinel限流熔斷

Sentinel概述

Sentinel (分布式系統的流量防衛兵) 是阿裡開源的一套用于服務容錯的綜合性解決方案。它以流量為切入點, 從流量控制、熔斷降級、系統負載保護等多個次元來保護服務的穩定性。

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

Sentinel核心分為兩個部分:

  • 核心庫(Java 用戶端):能夠運作于所有 Java 運作時環境,同時對Dubbo /Spring Cloud 等架構也有較好的支援。
  • 控制台(Dashboard):基于 Spring Boot 開發,打包後可以直接運作。

Sentinel的使用分為兩部分:

  • entinel-dashboard:與hystrix-dashboard類似,但是它更為強大一些。除了與hystrix-dashboard一樣提供實時監控之外,還提供了流控規則、熔斷規則的線上維護等功能。
  • 用戶端整合:每個微服務用戶端都需要整合sentinel的用戶端封裝與配置,才能将監控資訊上報給dashboard展示以及實時的更改限流或熔斷規則等。

安裝Sentinel服務

Sentinel 提供一個輕量級的控制台, 它提供機器發現、單機資源實時監控以及規則管理等功能,其控制台安裝步驟如下:

第一步:打開sentinel下載下傳網址

https://github.com/alibaba/Sentinel/releases

第二步:下載下傳Jar包(可以存儲到一個sentinel目錄)

第三步:在sentinel對應目錄,打開指令行(cmd),啟動運作sentinel

java -Dserver.port=8180 -Dcsp.sentinel.dashboard.server=localhost:8180 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.1.jar
           

檢測啟動過程,如圖所示:

SpringCloud微服務元件:Sentinel限流熔斷

通路Sentinal服務

第一步:假如Sentinal啟動ok,通過浏覽器進行通路測試,登陸sentinel,預設使用者和密碼都是sentinel,如圖所示:

SpringCloud微服務元件:Sentinel限流熔斷

sentinel-dashboard不像Nacos的服務端那樣提供了外置的配置檔案,比較容易修改參數。不過不要緊,由于sentinel-dashboard是一個标準的spring boot應用,是以如果要自定義端口号等内容的話,可以通過在啟動指令中增加參數來調整,比如:-Dserver.port=8888。

預設情況下,sentinel-dashboard以8080端口啟。

對于使用者登入的相關配置可以在啟動指令中增加下面的參數來進行配置:

  • -Dsentinel.dashboard.auth.username=sentinel: 用于指定控制台的登入使用者名為 sentinel;
  • -Dsentinel.dashboard.auth.password=123456: 用于指定控制台的登入密碼為 123456;
  • 如果省略這兩個參數,預設使用者和密碼均為 sentinel
  • -Dserver.servlet.session.timeout=7200: 用于指定 Spring Boot 服務端 session 的過期時間,如 7200 表示 7200 秒;60m 表示 60 分鐘,預設為 30 分鐘;

Sentinel快速入門

第一步:Sentinel 應用于服務提供方(provider),在服務提供方添加依賴如下:

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
           

第二步:打開服務提供方配置檔案xxx.yml,添加sentinel配置,代碼如下:

spring:
  cloud:
    sentinel:
      transport:
         dashboard: localhost:8180 # 指定sentinel控制台位址。
           

第三步:建立一個用于示範限流操作的Controller對象,例如:

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/provider")
public class ProviderSentinelController {
       @GetMapping("/sentinel01")
       public String doSentinel01(){
           return "sentinel 01 test  ...";
       }
}
           

啟動sca-provider服務,然後對指定服務進行通路,如圖所示:

SpringCloud微服務元件:Sentinel限流熔斷

Sentinel限流入門實踐

我們設定一下指定接口的流控(流量控制),QPS(每秒請求次數)單機門檻值為1,代表每秒請求不能超出1次,要不然就做限流處理,處理方式直接調用失敗。

第一步:選擇要限流的鍊路,如圖所示:

SpringCloud微服務元件:Sentinel限流熔斷

第二步:設定限流政策,如圖所示:

SpringCloud微服務元件:Sentinel限流熔斷

第三步:反複重新整理通路消費端端服務,檢測是否有限流資訊輸出,如圖所示:

SpringCloud微服務元件:Sentinel限流熔斷

Sentinel流控規則分析

門檻值類型

  • QPS(Queries Per Second) :當調用相關url對應的資源時,QPS達到單機門檻值時,就會限流。
  • 線程數:當調用相關url對應的資源時,線程數達到單機門檻值時,就會限流。

設定限流模式

Sentinel的流控模式代表的流控的方式,預設【直接】,還有關聯,鍊路。

直接模式

Sentinel預設的流控處理就是【直接->快速失敗】。

SpringCloud微服務元件:Sentinel限流熔斷

關聯模式

當關聯的資源達到指定門檻值,就限流自己。

例如設定了關聯資源為ur2時,假如關聯資源url2的QPS閥值超過1時,就限流url1接口(是不是感覺很霸道,關聯資源達到閥值,是本資源接口被限流了)。

這種關聯模式有什麼應用場景呢?我們舉個例子,訂單服務中會有2個重要的接口,一個是讀取訂單資訊接口,一個是寫入訂單資訊接口。在高并發業務場景中,兩個接口都會占用資源,如果讀取接口通路過大,就會影響寫入接口的性能。

業務中如果我們希望寫入訂單比較重要,要優先考慮寫入訂單接口。那就可以利用關聯模式;在關聯資源上面設定寫入接口,資源名設定讀取接口就行了;這樣就起到了優先寫入,一旦寫入請求多,就限制讀的請求。例如

第一步:在ProviderSentinelController中添加一個方法,例如:

@GetMapping("/sentinel02")
   public String doSentinel02(){
     return "sentinel 02 test  ...";
   }
           

第二步:在sentinel中做限流設計,

例如

SpringCloud微服務元件:Sentinel限流熔斷

第三步:打開兩個測試視窗,對/provider/sentinel02進行通路,檢查/provider/sentinel01的狀态,例如:

SpringCloud微服務元件:Sentinel限流熔斷

鍊路模式

鍊路模式隻記錄指定鍊路入口的流量。也就是當多個服務對指定資源調用時,假如流量超出了指定門檻值,則進行限流。

被調用的方法用@SentinelResource進行注解,然後分别用不同業務方法對此業務進行調用,假如A業務設定了鍊路模式的限流,在B業務中是不受影響的。現在對鍊路模式做一個實踐,例如:

第一步:在指定service包建立一個ResourceService類,代碼如下:

@Service
public class ResourceService{
    @SentinelResource("doGetResource")
    public String doGetResource(){
        return "doGetResource";
    }
}
           

第二步:在ProviderSentinelController中添加一個方法,例如:

@Autowired
private ResourceService resourceService;
@GetMapping("/sentinel03")
public String doSentinel03() throws InterruptedException {
    resourceService.doGetResource();
    return "sentinel 03 test";
}
           

第三步:在sentinel中配置限流規則,例如:

SpringCloud微服務元件:Sentinel限流熔斷
SpringCloud微服務元件:Sentinel限流熔斷

設定鍊路流控規則後,再頻繁對限流鍊路進行通路,檢測是否會出現500異常,例如

SpringCloud微服務元件:Sentinel限流熔斷

說明,流控模式為鍊路模式時,假如是sentinel 1.7.2以後版本,Sentinel Web過濾器預設會聚合所有URL的入口為sentinel_spring_web_context,是以單獨對指定鍊路限流會不生效,需要在springboot配置檔案application.yml中,添加如下語句來關閉URL PATH聚合,例如:

sentinel:
     web-context-unify: false
           

當設定了這個配置後,啟動服務,就可以對指定的特定鍊路進行限流了。

還有,當我們也可以基于@SentinelResource注解描述的方法進行限流後的異常進行自定義處理,其步驟如下:

第一步:定義blockHandlerClass,例如:

package com.jt.provider.service;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class ResourceBlockHandler {
    /**
     * 限流後的異常處理方法,應用于@SentinelResource注解中,
     * 此方法在編寫時有如下幾個要求:
     * 1)方法修飾符為public
     * 2)必須為static方法
     * 3)傳回值類型與@SentinelResource注解描述的方法相同
     * 4)參數類型為BlockException
     * 5)方法名自己定義
     * @param ex
     * @return
     */
    public static String doHandle(BlockException ex){
        log.error("block exception {}", ex.getMessage());
        return "通路太頻繁了,稍等片刻再通路";
    }
}
           

第二步:修改@SentinelResource注解中的屬性定義,例如:

@SentinelResource(value="doGetResource",
        blockHandlerClass = ResourceBlockHandler.class,
        blockHandler = "doHandle")
public String doGetResource(){
    return "do get resource";
}
           

第三步:在controller方法中,調用@Sentinel注解描述的方法,例如:

/**
 * 示範鍊路限流
 * @return
 */
@GetMapping("/sentinel03")
public String doSentinel03(){
   return resourceService.doGetResource();
   //return "sentinel 03 test";
}
           

Sentinel 熔斷降級

除了流量控制以外,對調用鍊路中不穩定的資源進行熔斷降級也是保障高可用的重要措施之一。由于調用關系的複雜性,如果調用鍊路中的某個資源不穩定,最終會導緻請求發生堆積。

Sentinel 熔斷降級會在調用鍊路中某個資源出現不穩定狀态時(例如調用逾時或異常比例升高),對這個資源的調用進行限制,讓請求快速失敗,避免影響到其它的資源而導緻級聯錯誤。當資源被降級後,在接下來的降級時間視窗之内,對該資源的調用都自動熔斷(預設行為是抛出DegradeException)。

準備工作

在ProviderController 類中添加doSentinel05方法,基于此方法示範慢調用過程下的限流,代碼如下:

//AtomicLong 類支援線程安全的自增自減操作
  private AtomicLong atomicLong=new AtomicLong(1);
  @GetMapping("/sentinel05")
  public  String doSentinel05() throws InterruptedException {
      //擷取自增對象的值,然後再加1
      long num=atomicLong.getAndIncrement();
      if(num%2==0){//模拟50%的慢調用比例
         Thread.sleep(200);
      }
      return "sentinel 04 test";
  }
           

說明,我們在此方法中設定休眠,目的是為了示範慢調用(響應時間比較長).

Sentinel降級入門

接下來,我們基于一個請求鍊路,進行服務降級及應用實踐,例如:

第一步:服務啟動後,選擇要降級的鍊路,如圖所示:

SpringCloud微服務元件:Sentinel限流熔斷

第二步:選擇要降級的鍊路,如圖所示:

SpringCloud微服務元件:Sentinel限流熔斷

這裡表示熔斷政策選擇"慢調用比例",表示請求數超過3時,假如平均響應時間超過200毫秒的有30%,則對請求進行熔斷,熔斷時長為20秒鐘,10秒以後恢複正常。

第三步:對指定鍊路進行重新整理,多次通路測試,檢測頁面上是否會出現限流(預設異常為DegradeException)

SpringCloud微服務元件:Sentinel限流熔斷

我們也可以進行斷點調試,在DefaultBlockExceptionHandler中的handle方法内部加斷點,分析異常類型,假如異常類型DegradeException 則為降級熔斷。

Sentinel 異常處理

系統提供了預設的異常處理機制,假如預設處理機制不滿足我們需求,我們可以自己進行定義。定義方式上可以直接或間接實作BlockExceptionHandler接口,并将對象交給spring管理。

package com.jt.provider.controller;

import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;

/**
 * 自定義限流,降級等異常處理對象
 */
@Slf4j
@Component
public class ServiceBlockExceptionHandler
     implements BlockExceptionHandler {
    /**
     * 用于處理BlockException類型以及子類類型異常
     */
    @Override
    public void handle(HttpServletRequest request,
                       HttpServletResponse response,
                       BlockException e) throws Exception {
        //設定響應資料編碼
        response.setCharacterEncoding("utf-8");
        //告訴用戶端響應資料的類型,以及用戶端顯示内容的編碼
        response.setContentType("text/html;charset=utf-8");
        //向用戶端響應一個json格式的字元串
        //String str="{\"status\":429,\"message\":\"通路太頻繁了\"}";
        Map<String,Object> map=new HashMap<>();
        map.put("status", 429);
        map.put("message","通路太頻繁了");
        String jsonStr=new ObjectMapper().writeValueAsString(map);
        PrintWriter out = response.getWriter();
        out.print(jsonStr);
        out.flush();
        out.close();
    }
}
           

Sentinel熱點規則分析

  • 何為熱點?熱點即經常通路的資料。比如:
    • 商品 ID 為參數,統計一段時間内最常購買的商品 ID 并進行限制。
    • 使用者 ID 為參數,針對一段時間内頻繁通路的使用者 ID 進行限制。
  • 熱點參數限流會統計傳入參數中的熱點資料,并根據配置的限流門檻值與模式,對包含熱點參數的資源調用進行限流。熱點參數限流可以看做是一種特殊的流量控制,僅對包含熱點參數的資源調用生效。其中,Sentinel會利用 LRU 政策統計最近最常通路的熱點參數,結合令牌桶算法來進行參數級别的流控。
  • 注意的是,熱點參數限流對預設的SpringMVC資源無效,隻有使用SentinelResource标注的資源有用。

第一步:在sca-provider中添加如下方法,例如:

@GetMapping("/sentinel/findById")
@SentinelResource("resource")
public String doFindById(@RequestParam("id") Integer id){
    return "resource id is "+id;
}
           

第二步:服務啟動後,選擇要限流的熱點鍊路,如圖所示:

SpringCloud微服務元件:Sentinel限流熔斷

第三步:設定要限流的熱點,如圖所示:

SpringCloud微服務元件:Sentinel限流熔斷

熱點規則的限流模式隻有QPS模式(這才叫熱點)。參數索引為@SentinelResource注解的方法參數下标,0代表第一個參數,1代表第二個參數。單機門檻值以及統計視窗時長表示在此視窗時間超過門檻值就限流。

第四步:多次通路熱點參數方法,前端會出現如下界面,如圖所示:

SpringCloud微服務元件:Sentinel限流熔斷

然後,在背景出現如下異常表示限流成功。

com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException: 2
           

其中,熱點參數其實說白了就是特殊的流控,流控設定是針對整個請求的;但是熱點參數他可以設定到具體哪個參數,甚至參數針對的值,這樣更靈活的進行流控管理。

一般應用在某些特殊資源的特殊處理,如:某些商品流量大,其他商品流量很正常,就可以利用熱點參數限流的方案。

特定參數【熱點限流中的某個參數值的門檻值設計】設計

配置參數例外項,如圖所示:

SpringCloud微服務元件:Sentinel限流熔斷

其中,這裡表示參數值為5時門檻值為100,其它參數值門檻值為1.

Sentinel系統規則

  • 系統在生産環境運作過程中,我們經常需要監控伺服器的狀态,看伺服器CPU、記憶體、IO等的使用率;主要目的就是保證伺服器正常的運作,不能被某些應用搞崩潰了;而且在保證穩定的前提下,保持系統的最大吞吐量。
  • sentinel中的系統規則,是對所有鍊路的控制規則,是一種系統保護政策,Sentinel系統保護規則被觸發以後底層會抛出SystemBlockException異常。
  • Sentinel的系統保護規則是從應用級别的入口流量進行控制,從單台機器的總體 Load(負載)、RT(響應時間)、入口 QPS 、線程數和CPU使用率五個次元監控應用資料,讓系統盡可能跑在最大吞吐量的同時保證系統整體的穩定性。如圖所示:
SpringCloud微服務元件:Sentinel限流熔斷

系統規則是一種全局設計規則,其中,

  • Load(僅對 Linux/Unix-like 機器生效):當系統 load1 超過門檻值,且系統目前的并發線程數超過系統容量時才會觸發系統保護。系統容量由系統的 maxQps * minRt 計算得出。設定參考值一般是 CPU cores * 2.5。
  • CPU使用率:當系統 CPU 使用率超過門檻值即觸發系統保護(取值範圍 0.0-1.0)。
  • RT:當單台機器上所有入口流量的平均 RT 達到門檻值即觸發系統保護,機關是毫秒。
  • 線程數:當單台機器上所有入口流量的并發線程數達到門檻值即觸發系統保護。
  • 入口 QPS:當單台機器上所有入口流量的 QPS 達到門檻值即觸發系統保護。

說明,系統保護規則是應用整體次元的,而不是資源次元的,并且僅對入口流量生效。入口流量指的是進入應用的流量(EntryType.IN),比如 Web 服務。

Sentinel授權規則

很多時候,我們需要根據調用方來限制資源是否通過,這時候可以使用 Sentinel 的黑白名單控制的功能。黑白名單根據資源的請求來源(origin)限制資源是否通過,若配置白名單則隻有請求來源位于白名單内時才可通過;若配置黑名單則請求來源位于黑名單時不通過,其餘的請求通過。例如微信中的黑名單。

快速入門

sentinel可以基于黑白名單方式進行授權規則設計,如圖所示:

SpringCloud微服務元件:Sentinel限流熔斷

黑白名單規則(AuthorityRule)非常簡單,主要有以下配置項:

  • 資源名:即限流規則的作用對象
  • 流控應用:對應的黑名單/白名單中設定的規則值,多個值用逗号隔開.
  • 授權類型:白名單,黑名單(不允許通路).

案例實作:

定義請求解析器,用于對請求進行解析,并傳回解析結果,sentinel底層在攔截到使用者請求以後,會對請求資料基于此對象進行解析,判定是否符合黑白名單規則,例如:

第一步:定義RequestOriginParser接口的實作類,在接口方法中解析請求參數資料并傳回,底層會基于此傳回值進行授權規則應用。

@Component
public class DefaultRequestOriginParser implements RequestOriginParser {
    @Override
    public String parseOrigin(HttpServletRequest request) {
        String origin = request.getParameter("origin");//這裡的參數名會與請求中的參數名一緻
        return origin;
    }
}
           

第二步:定義流控規則,如圖所示:

SpringCloud微服務元件:Sentinel限流熔斷

第三步:執行資源通路,檢測授權規則應用,當我們配置的流控應用值為app1時,假如規則為黑名單,則基于http://ip:port/path?origin=app1的請求不可以通過,其請求處理流程如圖下:

SpringCloud微服務元件:Sentinel限流熔斷
拓展:嘗試基于請求ip等方式進行黑白名單的規則設計,例如:

第一步: 修改請求解析器,擷取請求ip并傳回,例如:

@Component
public class DefaultRequestOriginParser  implements RequestOriginParser {
    //解析請求源資料
    @Override
    public String parseOrigin(HttpServletRequest request) {
        //擷取通路請求中的ip位址,基于ip位址進行黑白名單設計(例如在流控應用欄寫ip位址)
        String ip= request.getRemoteAddr();
        System.out.println("ip="+ip);
        return ip;
    }//授權規則中的黑白名單的值,來自此方法的傳回值
}
           

第二步:在sentinel控制台定義授權規則,例如:

SpringCloud微服務元件:Sentinel限流熔斷

第三步:規則定義後以後,基于你的ip位址,進行通路測試,檢測黑白名單效果.

  • Sentinel中的授權規則: 對指定資源的通路給出的一種簡易的授權政策
  • Sentinel如何識别白名單和黑名單?(在攔截器中通過調用RequestOriginParser對象的方法檢測具體的規則)
  • 授權規則中RequestOriginParser類的做用是什麼?(對流控應用值進行解析,檢查服務通路時傳入的值是否與RequestOriginParser的parseOrigin方法傳回值是否相同。)