日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

gateway sentinel 熔断 不起作用_Sentinel 的一些概念与核心类介绍

發布時間:2024/9/15 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 gateway sentinel 熔断 不起作用_Sentinel 的一些概念与核心类介绍 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

了解 Sentinel 的一些概念

  • 資源:資源是 Sentinel 的關鍵概念。資源,可以是一個方法、一段代碼、由應用提供的接口,或者由應用調用其它應用的接口。
  • 規則:圍繞資源的實時狀態設定的規則,包括流量控制規則、熔斷降級規則以及系統保護規則、自定義規則。
  • 降級:在流量劇增的情況下,為保證系統能夠正常運行,根據資源的實時狀態、訪問流量以及系統負載有策略的拒絕掉一部分流量。

Sentinel 資源指標數據統計相關的類

Sentinel 中指標數據統計以資源為維度。資源使用 ResourceWrapper 對象表示,我們把 ResourceWrapper 對象稱為資源 ID。如果一個資源描述的是一個接口,那么資源名稱通常就是接口的 url,例如“GET:/v1/demo”。

ResourceWrapper

public abstract class ResourceWrapper { protected final String name; protected final EntryType entryType; protected final int resourceType; public ResourceWrapper(String name, EntryType entryType, int resourceType) { this.name = name; this.entryType = entryType; this.resourceType = resourceType; }}復制

ResourceWrapper 有三個字段:

  • name 為資源名稱,例如:“GET:/v1/demo”。
  • entryType 為流量類型,即流入流量還是流出流量,通俗點說就是發起請求還是接收請求。
  • resourceType 表示資源的類型,例如 Dubbo RPC、Web MVC 或者 API Gateway 網關。

EntryType 是一個枚舉類型:

public enum EntryType { IN("IN"), OUT("OUT");}復制

可以把 IN 和 OUT 簡單理解為接收處理請求與發送請求。當接收到別的服務或者前端發來的請求,那么 entryType 為 IN;當向其他服務發起請求時,那么 entryType 就為 OUT。例如,在消費端向服務提供者發送請求,當請求失敗率達到多少時觸發熔斷降級,那么服務消費端為實現熔斷降級就需要統計資源的 OUT 類型流量。

Sentinel 目前支持的資源類型有以下幾種:

public final class ResourceTypeConstants { public static final int COMMON = 0; public static final int COMMON_WEB = 1; public static final int COMMON_RPC = 2; public static final int COMMON_API_GATEWAY = 3; public static final int COMMON_DB_SQL = 4;}復制
  • COMMON:默認
  • COMMON_WEB:Web 應用的接口
  • COMMON_RPC:Dubbo 框架的 RPC 接口
  • COMMON_API_GATEWAY:用于 API Gateway 網關
  • COMMON_DB_SQL:數據庫 SQL 操作

Node

Node 用于持有實時統計的指標數據,Node 接口定義了一個 Node 類所需要提供的各項指標數據統計的相關功能,為外部屏蔽滑動窗口的存在。提供記錄請求被拒絕、請求被放行、請求處理異常、請求處理成功的方法,以及獲取當前時間窗口統計的請求總數、平均耗時等方法。Node 接口源碼如下。

public interface Node extends OccupySupport, DebugSupport { long totalRequest(); // 獲取總的請求數 long totalPass(); // 獲取通過的請求總數 long totalSuccess(); // 獲取成功的請求總數 long blockRequest(); // 獲取被 Sentinel 拒絕的請求總數 long totalException(); // 獲取異常總數 double passQps(); // 通過 QPS double blockQps(); // 拒絕 QPS double totalQps(); // 總 qps double successQps(); // 成功 qps double maxSuccessQps(); // 最大成功總數 QPS(例如秒級滑動窗口的數組大小默認配置為 2,則取數組中最大) double exceptionQps(); // 異常 QPS double avgRt(); // 平均耗時 double minRt(); // 最小耗時 int curThreadNum(); // 當前并發占用的線程數 double previousBlockQps(); // 前一個時間窗口的被拒絕 qps double previousPassQps(); // 前一個時間窗口的通過 qps Map metrics(); List rawMetricsInMin(Predicate timePredicate); void addPassRequest(int count); // 添加通過請求數 void addRtAndSuccess(long rt, int success); // 添加成功請求數,并且添加處理成功的耗時 void increaseBlockQps(int count); // 添加被拒絕的請求數 void increaseExceptionQps(int count); // 添加異常請求數 void increaseThreadNum(); // 自增占用線程 void decreaseThreadNum(); // 自減占用線程 void reset(); // 重置滑動窗口}復制

它的幾個實現類:DefaultNode、ClusterNode、EntranceNode、StatisticNode 的關系如下圖所示。

StatisticNode

Statistic 即統計的意思,StatisticNode 是 Node 接口的實現類,是實現實時指標數據統計 Node。

public class StatisticNode implements Node { // 秒級滑動窗口,2 個時間窗口大小為 500 毫秒的 Bucket private transient volatile Metric rollingCounterInSecond = new ArrayMetric(2,1000); // 分鐘級滑動窗口,60 個 Bucket 數組,每個 Bucket 統計的時間窗口大小為 1 秒 private transient Metric rollingCounterInMinute = new ArrayMetric(60, 60 * 1000, false); // 統計并發使用的線程數 private LongAdder curThreadNum = new LongAdder();}復制

如代碼所示,一個 StatisticNode 包含一個秒級和一個分鐘級的滑動窗口,以及并行線程數計數器。秒級滑動窗口用于統計實時的 QPS,分鐘級的滑動窗口用于保存最近一分鐘內的歷史指標數據,并行線程計數器用于統計當前并行占用的線程數。

StatisticNode 的分鐘級和秒級滑動窗口統計的指標數據分別有不同的用處。例如,StatisticNode 記錄請求成功和請求執行耗時的方法中調用了兩個滑動窗口的對應指標項的記錄方法,代碼如下:

@Override public void addRtAndSuccess(long rt, int successCount) { // 秒級滑動窗口 rollingCounterInSecond.addSuccess(successCount); rollingCounterInSecond.addRT(rt); // 分鐘級滑動窗口 rollingCounterInMinute.addSuccess(successCount); rollingCounterInMinute.addRT(rt); }復制

獲取前一秒被 Sentinel 拒絕的請求總數從分鐘級滑動窗口獲取,代碼如下:

@Overridepublic double previousBlockQps() { return this.rollingCounterInMinute.previousWindowBlock();}復制

而獲取當前一秒內已經被 Sentinel 拒絕的請求總數則從秒級滑動窗口獲取,代碼如下:

@Overridepublic double blockQps() { return rollingCounterInSecond.block() / rollingCounterInSecond.getWindowIntervalInSec();}復制

獲取最小耗時也是從秒級的滑動窗口取的,代碼如下:

@Override public double minRt() { // 秒級滑動窗口 return rollingCounterInSecond.minRt(); }復制

由于方法比較多,這里就不詳細介紹每個方法的實現了。

StatisticNode 還負責統計并行占用的線程數,用于實現信號量隔離,按資源所能并發占用的最大線程數實現限流。當接收到一個請求就將 curThreadNum 自增 1,當處理完請求時就將 curThreadNum 自減一,如果同時處理 10 個請求,那么 curThreadNum 的值就為 10。

假設我們配置 tomcat 處理請求的線程池大小為 200,通過控制并發線程數實現信號量隔離的好處就是不讓一個接口同時使用完這 200 個線程,避免因為一個接口響應慢將 200 個線程都阻塞導致應用無法處理其他請求的問題,這也是實現信號量隔離的目的。

DefaultNode

DefaultNode 是實現以資源為維度的指標數據統計的 Node,是將資源 ID 和 StatisticNode 映射到一起的 Node。

public class DefaultNode extends StatisticNode { private ResourceWrapper id; private volatile Set childList = new HashSet<>(); private ClusterNode clusterNode; public DefaultNode(ResourceWrapper id, ClusterNode clusterNode) { this.id = id; this.clusterNode = clusterNode; }}復制

如代碼所示,DefaultNode 是 StatisticNode 的子類,構造方法要求傳入資源 ID,表示該 Node 用于統計哪個資源的實時指標數據,指標數據統計則由父類 StatisticNode 完成。

DefaultNode 字段說明:

  • id:資源 ID,ResourceWrapper 對象。
  • childList:childList 是一個 Node(DefaultNode)集合,用于存放子節點。
  • clusterNode:clusterNode 字段是一個 ClusterNode,ClusterNode 也是 StatisticNode 的子類。

我們回顧下 Sentinel 的基本使用:

ContextUtil.enter("上下文名稱,例如:sentinel_spring_web_context");Entry entry = null;try { entry = SphU.entry("資源名稱,例如:/rpc/openfein/demo", EntryType.IN (或者 EntryType.OUT)); // 執行業務方法 return doBusiness();} catch (Exception e) { if (!(e instanceof BlockException)) { Tracer.trace(e); } throw e;} finally { if (entry != null) { entry.exit(1); } ContextUtil.exit();}復制

如上代碼所示,doBusiness 業務方法被 Sentinel 保護,當 doBusiness 方法被多層保護時,就可能對同一個資源創建多個 DefaultNode。一個資源理論上可能有多個 DefaultNode,是否有多個 DefaultNode 取決于是否存在多個 Context,即當前調用鏈路上是否多次調用 ContextUtil#enter 方法,是否每次調用 ContextUtil#enter 方法都會創建一個 Context。

特別加上“理論上”,是因為在一個線程中,調用多次 ContextUtil#enter 方法,只有第一次調用會創建 Context,ContextUtil 使用 ThreadLocal 存儲 Context,所以后續的調用都會使用之前創建的 Context,而 DefaultNode 是在 NodeSelectorSlot 中創建的,使用 Map 緩存,key 為 Context#name,所以在使用同一個 Context 的情況下,只會為一個資源創建一個 DefaultNode。這部分內容在介紹 NodeSelectorSlot 時再作詳細介紹。

ClusterNode

Sentinel 使用 ClusterNode 統計每個資源全局的指標數據,以及統計該資源按調用來源區分的指標數據。全局數據指的是不區分調用鏈路,一個資源 ID 只對應一個 ClusterNode。

public class ClusterNode extends StatisticNode { // 資源名稱 private final String name; // 資源類型 private final int resourceType; // 來源指標數據統計 private Map originCountMap = new HashMap<>(); // 控制并發修改 originCountMap 用的鎖 private final ReentrantLock lock = new ReentrantLock(); public ClusterNode(String name, int resourceType) { this.name = name; this.resourceType = resourceType; }} 復制

ClusterNode 字段說明:

  • name:資源名稱。很奇怪,這里沒有使用 ResourceWrapper,是版本歷史問題,還是因為 ClusterNode 不需要判斷流量類型。
  • resourceType:資源類型。
  • originCountMap:維護每個調用來源的指標數據統計數據(StatisticNode),其用途是什么,在使用到時再做分析。

EntranceNode

EntranceNode 是一個特殊的 Node,它繼承 DefaultNode,用于維護一顆樹,從根節點到每個葉子節點都是不同請求的調用鏈路,所經過的每個節點都對應著調用鏈路上被 Sentinel 保護的資源,一個請求調用鏈路上的節點順序正是資源被訪問的順序。

public class EntranceNode extends DefaultNode { public EntranceNode(ResourceWrapper id, ClusterNode clusterNode) { super(id, clusterNode); }}復制

在一個 Web MVC 應用中,每個接口就是一個資源,Sentinel 通過 Spring MVC 攔截器攔截每個接口的入口,統一創建名為“sentinel_spring_web_context”的 Context,名稱相同的 Context 都使用同一個 EntranceNode。一個 Web 應用可能有多個接口,而 childList 就用于存儲每個接口對應的 DefaultNode。

如果想統計一個應用的所有接口(不一定是所有,沒有被調用過的接口不會創建對應的 DefaultNode)總的 QPS,只需要調用 EntranceNode 的 totalQps 就能獲取到。EntranceNode 的 totalQps 方法代碼如下:

@Override public double totalQps() { double r = 0; // 遍歷 childList for (Node node : getChildList()) { r += node.totalQps(); } return r; }復制

EntranceNode、DefaultNode、ClusterNode 與滑動窗口的關系如下圖所示:

Sentinel 中的 Context 與 Entry

理解 Context 與 Entry 也是理解 Sentinel 整個工作流程的關鍵,其中 Entry 還會涉及到“調用樹”這一概念。

Context

Context 代表調用鏈路上下文,貫穿一次調用鏈路中的所有 Entry。Context 維持著入口節點(entranceNode)、本次調用鏈路的 curNode、調用來源(origin)等信息。Context 名稱即為調用鏈路入口名稱。

Context 通過 ThreadLocal 傳遞,只在調用鏈路的入口處創建。

假設服務B提供一個查詢天氣預報的接口給服務A調用,服務B實現查詢天氣預報的接口是調用第三方服務C實現的,服務B是一個 MVC 應用,同時服務B調用服務 C 接口使用 OpenFeign 實現 RPC 調用。那么,服務 B 即使用了 Sentinel 的 MVC 適配模塊,也使用了 Sentinel 的 OpenFeign 適配模塊。

當服務 B 接收到服務 A 請求時,會創建一個名為“sentinel_spring_web_context”的 Context,服務 B 在向服務 C 發起接口調用時由于當前線程已經存在一個 Context,所以還是用“sentinel_spring_web_context”這個 Context,代表是同一個調用鏈路入口。

舉個不恰當的例子:

  • 路徑一:A.a()-> 調用 -> B.b()-> 調用 C.c(),A.a() 為該調用鏈路的入口,入口名稱為“a_context”。
  • 路徑二:D.d()-> 調用 -> B.b()-> 調用 C.c(),D.d() 為該調用鏈路的入口,入口名稱為“d_context”。

那么,每次調用 A.a() 方法都會創建名為“a_context”的 Context,每次調用 B.b() 方法都會創建名為“b_context”的 Context。如果 A.a() 同時有 20 個請求,那么就會創建 20 個名為“a_context”的 Context,Context 代表了這 20 個請求每個請求的調用鏈路上下文,而路徑一就是這 20 個請求相同的調用鏈路。

Context 的字段定義如下:

public class Context { private final String name; private DefaultNode entranceNode; private Entry curEntry; private String origin = ""; // 我們不討論異步的情況 // private final boolean async;}復制
  • name:Context 的名稱。
  • entranceNode:當前調用樹的入口節點,類型為 EntranceNode。同一個入口的資源,每個資源對應一個 DefaultNode,entranceNode#childList 用于存儲這些資源的 DefaultNode。
  • curEntry:當前 Entry(CtEntry)。
  • origin:調用來源的名稱,即服務消費者的名稱或者服務消費者的來源 IP,取決于服務消費者是否使用 Sentinel,由 Sentinel 適配層傳遞過來。例如:服務提供者是 Spring MVC 應用,且服務提供者使用 Sentinel 的 Web MVC 適配,那么 Sentinel 會嘗試從請求頭獲取"S-user",如果服務消費者有在請求頭傳遞這個參數,那么就能夠獲取到。

Entry(CtEntry)

在調用 Context#getCurNode 方法獲取調用鏈路上當前訪問到的資源的 DefaultNode 時,實際是從 Context#curEntry 獲取的,Entry 維護了當前資源的 DefaultNode,以及調用來源的 StatisticNode。Entry 抽象類字段的定義如下。

public abstract class Entry implements AutoCloseable { private static final Object[] OBJECTS0 = new Object[0]; private long createTime; // 當前節點(DefaultNode) private Node curNode; // 來源節點 private Node originNode; private Throwable error; // 資源 protected ResourceWrapper resourceWrapper;}復制

CtEntry 是 Entry 的直接子類,后面分析源碼時,我們所說 Entry 皆指 CtEntry。CtEntry 中聲明的字段信息如下代碼所示。

class CtEntry extends Entry { // 當前 Entry 指向的父 Entry protected Entry parent = null; // 父 Entry 指向當前 Entry protected Entry child = null; // 當前資源的 ProcessorSlotChain protected ProcessorSlot chain; // 當前上下文 protected Context context;}復制

CtEntry 用于維護父子 Entry,每一次調用 SphU#entry 方法都會創建一個 CtEntry。如果服務 B 在處理一個請求的路徑上會多次調用 SphU#entry,那么這些 CtEntry 會構成一個雙向鏈表。在每次創建 CtEntry,都會將 Context.curEntry 設置為這個新的 CtEntry,雙向鏈表的作用就是在調用 CtEntry#exit 方法時,能夠將 Context.curEntry 還原為上一個資源的 CtEntry。

例如,在服務 B 接收到服務 A 的請求時,會調用 SphU#entry 方法創建一個 CtEntry,我們取個代號 ctEntry1,此時的 ctEntry1 的父節點(parent)為空。當服務 B 向服務 C 發起調用時,OpenFeign 適配器調用 SphU#entry 的方法會創建一個 CtEntry,我們取個代號 ctEntry2,此時 ctEntry2 的父節點(parent)就是 ctEntry1,ctEntry1 的子節點(child)就是 ctEntry2,如下圖所示。

ROOT 與調用樹

Constants 常量類用于聲明全局靜態常量,Constants 有一個 ROOT 靜態字段,類型為 EntranceNode。

在調用 ContextUtil#enter 方法時,如果還沒有為當前入口創建 EntranceNode,則會為當前入口創建 EntranceNode,將其賦值給 Context.entranceNode,同時也會將這個 EntranceNode 添加到 Constants.ROOT 的子節點(childList)。資源對應的 DefaultNode 則是在 NodeSelectorSlot 中創建,并賦值給 Context.curEntry.curNode。

Constants.ROOT、Context.entranceNode 與 Entry.curNode 三者關系如下圖所示。

Sentinel 中的 ProcessorSlot

ProcessorSlot 直譯就是處理器插槽,是 Sentinel 實現限流降級、熔斷降級、系統自適應降級等功能的切入點。Sentinel 提供的 ProcessorSlot 可以分為兩類,一類是輔助完成資源指標數據統計的切入點,一類是實現降級功能的切入點。

輔助資源指標數據統計的 ProcessorSlot:

  • NodeSelectorSlot:為當前資源創建 DefaultNode,并且將 DefaultNode 賦值給 Context.curEntry.curNode(見倒數第二張圖);如果當前調用鏈路上只出現過一次 SphU#entry 的情況,將該 DefaultNode 添加到的 Context.entranceNode 的子節點(如倒數第一張圖所示,名為 sentinel_spring_web_context 的 EntranceNode),否則添加到 Context.curEntry.parent 的子節點(childList)。有點抽象,我們在分析 NodeSelectorSlot 源碼時再詳細介紹。
  • ClusterBuilderSlot:如果當前資源未創建 ClusterNode,則為資源創建 ClusterNode;將 ClusterNode 賦值給當前資源的 DefaultNode.clusterNode;如果調用來源(origin)不為空,則為調用來源創建 StatisticNode,用于實現按調用來源統計資源的指標數據,ClusterNode 持有每個調用來源的 StatisticNode。
  • StatisticSlot:這是 Sentinel 最為重要的類之一,用于實現指標數據統計。先是調用后續的 ProcessorSlot#entry 判斷是否放行請求,再根據判斷結果進行相應的指標數據統計操作。

實現降級功能的 ProcessorSlot:

  • AuthoritySlot:實現黑白名單降級
  • SystemSlot:實現系統自適應降級
  • FlowSlot:實現限流降級
  • DegradeSlot:實現熔斷降級

關于每個 ProcessorSlot 實現的功能,將在后續文章詳細分析。

總結

以上是生活随笔為你收集整理的gateway sentinel 熔断 不起作用_Sentinel 的一些概念与核心类介绍的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。