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

前言

什麼是雪崩問題?

微服務之間相互調用,因爲調用鏈中的一個服務故障,引起整個鏈路都無法訪問的情況。

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

前三種是一種已經出現後的應對措施可以避免因服務故障引起的雪崩問題;後一種是一個預防方案,可以避免因瞬間高併發流量而導致服務故障

與 Hystrix 對比

簇點鏈路:就是項目內的調用鏈路,鏈路中被監控的每個接口就是一個資源。默認情況下 sentinel 會監控 SpringMVC 的每一個端點(Endpoint),因此 SpringMVC 的每一個端點(Endpoint)就是調用鏈路中的一個資源。

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

Sentinel 概述

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

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

Sentinel 核心分爲兩個部分:
Sentinel 的使用分爲兩部分:
安裝 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

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

訪問 Sentinal 服務

第一步:假如 Sentinal 啓動 ok,通過瀏覽器進行訪問測試,登陸 sentinel,默認用戶和密碼都是 sentinel,如圖所示:

sentinel-dashboard不像 Nacos 的服務端那樣提供了外置的配置文件,比較容易修改參數。不過不要緊,由於sentinel-dashboard是一個標準的 spring boot 應用,所以如果要自定義端口號等內容的話,可以通過在啓動命令中增加參數來調整,比如:-Dserver.port=8888

默認情況下,sentinel-dashboard以 8080 端口啓。

對於用戶登錄的相關配置可以在啓動命令中增加下面的參數來進行配置:

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 服務,然後對指定服務進行訪問,如圖所示:

Sentinel 限流入門實踐

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

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

第二步:設置限流策略,如圖所示:

第三步:反覆刷新訪問消費端端服務,檢測是否有限流信息輸出,如圖所示:

Sentinel 流控規則分析

閾值類型

設置限流模式

Sentinel 的流控模式代表的流控的方式,默認【直接】,還有關聯,鏈路。

直接模式

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

關聯模式

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

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

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

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

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

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

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

例如

第三步:打開兩個測試窗口,對/provider/sentinel02進行訪問,檢查/provider/sentinel01的狀態,例如:

鏈路模式

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

被調用的方法用@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 中配置限流規則,例如:

設置鏈路流控規則後,再頻繁對限流鏈路進行訪問,檢測是否會出現 500 異常,例如

說明,流控模式爲鏈路模式時,假如是 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 降級入門

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

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

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

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

第三步:對指定鏈路進行刷新,多次訪問測試,檢測頁面上是否會出現限流(默認異常爲DegradeException

我們也可以進行斷點調試,在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 熱點規則分析

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

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

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

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

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

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

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

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

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

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

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

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

其中, 這裏表示參數值爲 5 時閾值爲 100,其它參數值閾值爲 1.

Sentinel 系統規則

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

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

Sentinel 授權規則

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

快速入門

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

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

案例實現:

定義請求解析器, 用於對請求進行解析, 並返回解析結果, sentinel 底層在攔截到用戶請求以後, 會對請求數據基於此對象進行解析, 判定是否符合黑白名單規則, 例如:

第一步: 定義RequestOriginParser接口的實現類,在接口方法中解析請求參數數據並返回, 底層會基於此返回值進行授權規則應用。

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

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

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

拓展:嘗試基於請求 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 控制檯定義授權規則, 例如:

第三步: 規則定義後以後, 基於你的 ip 地址, 進行訪問測試, 檢測黑白名單效果.

來源:blog.csdn.net/qq_25447799/article/

details/123122820

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/MXIsrdYLN7XvYZaCY-imfw