什麼是收斂
ARMS探針在運行過程中會採集很多指標資料(例如請求量、耗時、錯誤數等),為了能夠提供豐富、準確的監控資訊,指標中會攜帶一些維度資訊(例如機器、介面等)。由於各種原因,有些維度會出現發散的情況,即高基數問題。高基數問題不僅會給儲存系統帶來巨大的壓力,造成寫入丟失、查詢緩慢等問題,也會導致使用者的賬單暴漲。為解決該問題,ARMS提供了相應的收斂機制,一方面可用於解決高基數問題本身,即將維度基數控制在合理的範圍內,另一方面也可用於標記真實的觀測維度。例如使用者對外提供了RESTful形式的使用者服務,其中包含一個查詢使用者資訊的/api/v1/users/{ID}/info介面,如果直接記錄原始的請求URL,由於ID的變化,將會記錄一堆不同的URL,顯然這會造成高基數問題,另外還會導致使用者無法直觀地擷取到介面效能指標。本文將逐一介紹ARMS現有的收斂機制,以協助您瞭解收斂發生的原因及收斂結果所代表的含義。
收斂結果說明
ARMS已有的收斂結果及對應的原因概括如下,更詳細的部分請參見下文。
收斂結果 | 收斂原因 |
{ARMS_IP}:80 | 訪問同一連接埠的IP數量過多,超過閾值(預設50)。 |
{ARMS_STATIC_REQ} 或 {ARMS_S_XXX} | URL為靜態資源相關的請求。 |
{ARMS_ATTACK_REQ} | URL中包含攻擊字串。 |
{ARMS_PARAMED_REQ} | URL中攜帶了參數。 |
{ARMS_OTHERS} | 單位時間內,記錄的維度值超過了允許的上限。 說明 預設上限閾值詳細請參見下文基數空間收斂。 |
{ARMS_NUMBER} | URL中以 |
{ARMS_WORD} | URL中以 |
{ARMS_ANY} | URL中以 |
{XXX} | 使用了SpringController中的註解。 |
含有*的字串 說明 僅適用於探針版本小於4.x的情況。 | 基於探針端的記憶體統計機制對發散的部分進行了收斂。 |
收斂機制說明
以下所有收斂機制都是預設開啟的。除基數空間收斂外,所有收斂機制均支援使用者手動關閉。
不同的收斂機制支援的資料類型不同,每個收斂機制都會註明所支援的資料類型。
Spring註解收斂
對傳統的Web API來說,直接以請求URL作為維度值不會有任何問題,但對於RESTful形式的API或URL中包含變數的API來說,直接記錄請求URL將會導致維度發散。因此,對於使用了Spring Web架構的應用,ARMS會嘗試提取相應的註解資訊(例如@RequestMapping)作為介面的維度值。
收斂邏輯
讀取Spring URL路由註解中的Path資訊。
收斂結果
路由註解中配置的Value。
支援的資料類型
URL:僅適用於對外提供服務的URL,對於外部URL不適用。
收斂發生位置
探針端
支援的探針版本
2.9.1.2及以上版本
樣本
下述APIController定義了一個擷取使用者資訊的介面,其中使用了Path Variable。在採集介面資料時,將提取其註解(/api/v1/user/{userId}/info)作為最終的介面。
@RestController
@RequestMapping("/api/v1")
public class APIController {
@RequestMapping("/user/{userId}/info")
public String getUserInfo(@PathVariable("userId") String userId) {
return "hello " + userId;
}
}
收斂結果:
/api/v1/user/1234/info
被收斂為/api/v1/user/{userId}/info
。
基於記憶體統計的收斂
對於未使用Spring Web架構的應用或註解提取失敗的情境,將使用基於記憶體統計的收斂演算法進行收斂。
僅適用於4.X以下版本的探針。
收斂邏輯
對每一個輸入使用預設的切分符號(/、=等)進行切割,識別出一組單詞N。
對每個位置的單詞,統計其不重複的個數(即基數),當個數超過設定閾值時,該位置的單詞就會變為
*
。
收斂結果
包含*
的字串
支援的資料類型
任意
收斂發生位置
探針端
支援的探針版本
2.x及以上版本且小於4.X版本
樣本
應用A中對外提供一個使用者資訊查詢介面,請求方式為/api/v1/user/${userId}/info
,其中$userId填寫具體的userId。由於存在大量的userId, 記憶體統計模組會發現$userId存在發散的情況,便會將其收斂
為 /api/v1/user/*/info
自訂收斂
該功能支援使用者自訂一些收斂規則,來滿足一些特定的收斂需求。
收斂邏輯
逐一匹配使用者配置的收斂規則,如果命中,則結束。
收斂結果
以使用者配置為準。
支援的資料類型
URL
收斂觸發位置
4.x及以上版本觸發於探針端
4.x以下版本觸發於服務端
支援的探針版本
所有
樣本
使用者配置的規則:匹配/api/v1/user/[\d]+/info
,收斂為/api/v1/getUserInfo
。
對於所有命中/api/v1/user/[\d]+/info
Regex的請求將都被收斂為/api/v1/getUserInfo
。
靜態資源收斂
正常情況來說,ARMS並不會監控靜態資源相關請求。由於一些歷史原因,老舊的探針會採集靜態資源相關的指標資料,鑒於大多數情況下靜態資源並不具備監控價值且資源URL變化頻繁,預設開啟收斂。
收斂邏輯
檢查請求URL尾碼是否命中預設的靜態資源副檔名,如命中則會被收斂。
預設的靜態資源副檔名:.log .7z .tgz .jpg .jpeg .png .gif .css .js .ico .woff2 .xml .svg .pdf .txt .text .ppt .word .xlsx .tar.gz .tar.bz2 .sh .yml .yaml .zip .log .gz .ttf .woff .eot .rar .properties
收斂結果
預設為{ARMS_STATIC_REQ}
,開啟進階選項時(如有需要可提工單開啟),收斂結果中將包含資源尾碼名。
支援的資料類型
URL
收斂觸發位置
4.x及以上版本觸發於探針端
4.x以下版本觸發於服務端
支援的探針版本
所有
樣本
/api/v1/hello.jpg
預設收斂為{ARMS_STATIC_REQ}
,開啟進階選項時,收斂為{ARMS_S_JPG}
。
攻擊請求收斂
根據經驗,使用者服務可能會受到一些莫名的攻擊請求(例如嘗試讀取/etc/passwd檔案),這些請求是被攻擊者構造出來的,變化頻繁,記錄下來會給儲存帶來較大壓力。
收斂邏輯
檢查URL中是否含有攻擊字元,如存在則會被收斂。
預設的攻擊字元:` $ \ ' !
收斂結果
{ARMS_ATTACK_REQ}
支援的資料類型
URL
收斂觸發位置
4.x及以上版本觸發於探針端
4.x以下版本觸發於服務端
支援的探針版本
所有
樣本
/app/v1/user/info?cmd=`more /etc/passwd`
收斂為{ARMS_ATTACK_REQ}
。
帶參請求收斂
預設情況下,探針在採集URL時並不會擷取參數資訊,但依然存在部分情境下採集到的URL中包含了查詢參數,由於查詢參數不可控,極易導致維度發散。
收斂邏輯
判斷URL中是否帶有查詢參數,如果存在則進行收斂。
預設的查詢參數分隔字元:; ? &
收斂結果
預設為{ARMS_PARAMED_REQ},開啟進階選項時(如有需要可提工單開啟),收斂結果將保留URL部分,參數部分使用{ARMS_REQ_PARAMS}進行替換。
支援的資料類型
URL
收斂觸發位置
4.x及以上版本觸發於探針端
4.x以下版本觸發於服務端
支援的探針版本
所有
樣本
/api/v1/user/info?userId=12345
預設收斂為{ARMS_PARAMED_REQ}
,開啟進階選項時,收斂為/api/v1/user/info?{ARMS_REQ_PARAMS}
。
無意義單詞收斂
一般來說,一個URL中如果含有長度過長的單詞或數字,該URL大機率會發散,為了盡量減少發散的情況。ARMS預設會對過長的單詞或數字進行替換。
收斂邏輯
以/
切分URL,結果記為term數組,遍曆term數組,針對每個term判斷其長度是否超過下述閾值,如果超過則會進行替換。
單詞最大長度:64
純數字最大長度:10
單詞中包含的數字最大長度:10
收斂結果
過長的數字被收斂為:{ARMS_NUMBER}
過長的單詞被收斂為:{ARMS_WORD}
單詞中包含的數字過長被收斂為:{ARMS_ANY}
支援的資料類型
URL
收斂觸發位置
4.x及以上版本觸發於探針端
4.x以下版本觸發於服務端
支援的探針版本
所有
樣本
/api/2024040710/hello2024040710
將被收斂為/api/{ARMS_NUMBER}/{ARMS_ANY}
。
智能收斂
經過上文幾個收斂動作後,依然有可能存在大量發散的URL被記錄下來,針對這部分URL,ARMS會周期性基於演算法自動計算出相應的收斂規則,然後替換掉原始的URL。
收斂邏輯
邏輯較為複雜,此處僅做簡單介紹。
基於演算法對樣本URL進行分組。
對每個分組內的URL Pattern進行收斂處理,產生收斂規則。
合并各分組的收斂規則。
收斂結果
發散部分如果為純數字,將被替換為{ARMS_NUMBER}。
發散部分如果為純字母,將被替換為{ARMS_WORD}。
發散部分如果同時包含數字和字母,將被替換為{ARMS_ANY}。
支援的資料類型
URL
收斂觸發位置
4.x及以上版本觸發於探針端
4.x以下版本觸發於服務端
支援的探針版本
所有
樣本
/api/product/1/info
/api/product/2/info
....
/api/product/N/info
服務端經過計算後,會產生收斂規則:/api/product/[\d]+/info
(正則匹配)
收斂結果為/api/product/{ARMS_NUMBER}/info
。
後續符合上述正則的請求,都將被收斂為/api/product/{ARMS_NUMBER}/info
。
SQL規整化
由於多種情況的存在(例如分庫分表、注釋、明文),探針很可能會採集到大量SQL。基於這種情況,ARMS預設會對每條SQL進行處理,替換掉可能發散的部分。
規整邏輯
邏輯較為複雜,此處僅做簡單介紹。
注釋移除。
明文替換。
分庫分表名替換。
...
規整結果
替換掉發散部分後的結果。
支援的資料類型
SQL
收斂發生位置
探針端
支援的探針版本
4.X 及以上版本
樣本
select * from cache_0 where ckey='23'
將會被收斂為:
select * from cache_{NUM} where ckey=?
IP收斂
如果應用依賴的外部服務眾多且直接使用IP進行訪問,將會導致探針採集到大量的外部IP,導致發散。
收斂邏輯
按照連接埠對IP進行分組。
統計分組後,每個連接埠內包含的IP數,如果超過閾值則收斂。預設閾值:50
收斂結果
{ARMS_IP}:port
支援的資料類型
IP
收斂觸發位置
4.x及以上版本觸發於探針端
4.x以下版本觸發於服務端
支援的探針版本
所有
樣本
1.1.1.1:8080
...
1.1.1.255:8080
將被收斂為{ARMS_IP}:8080
。
基數空間收斂
對於URL類型的維度來說,經過上文收斂機制的處理,基本可以解決高基數問題。但對於SQL等類型來說,即使我們儘可能剔除了可能發散的資訊,依然可能會記錄大量的維度值,為解決這一問題,ARMS對單位時間內可以記錄的維度值進行了控制,以保證不會出現發散問題。
收斂邏輯
邏輯較為複雜,此處僅做簡單介紹。
周期性產生大小固定的基數空間。
針對每一個維度值,首先判斷基數空間中是否已存在該值,如存在則原樣返回,否則嘗試放入基數空間,如果可以放入則原樣返回,否則返回{ARMS_OTHERS}。
收斂結果
超過空間限制的維度值收斂為{ARMS_OTHERS}。
預設閾值
觀測對象 | 閾值(個) |
URL介面 | 每小時500 |
調度任務 | 每小時1000 |
RPC介面 | 每小時1000 |
上遊介面 | 每小時200 |
正常SQL | 每小時100 |
慢SQL | 每小時100 |
外部請求URL | 每小時200 |
外部請求地址 | 每小時100 |
支援的資料類型
任意
收斂觸發位置
4.x及以上版本觸發於探針端
4.x以下版本觸發於服務端
支援的探針版本
所有
樣本
指標會記錄外部服務地址,假定外部服務地址如下,基數空間的大小為每小時100個記錄。
www.a1.com
www.a2.com
....
www.a1000.com
那麼每1小時,僅有前100個外部服務會被記錄下來,後續的外部服務地址都會被收斂為{ARMS_OTHERS}
。
執行順序
4.x以下版本
探針端
URL類型
Spring收斂 > 基於記憶體統計的收斂
SQL類型
基於記憶體統計的收斂
IP等其他類型
基於記憶體統計的收斂
服務端
URL類型
自訂收斂 > 攻擊請求收斂 > 帶參請求收斂 > 靜態資源收斂 > 無意義單詞收斂 > 智能收斂> 基數空間收斂
SQL類型
自訂收斂 > 基數空間收斂
IP等其他類型
自訂收斂 > IP收斂 > 基數空間收斂
4.x及以上版本
探針端
URL類型
Spring收斂 > 自訂收斂 > 攻擊請求收斂 > 帶參請求收斂 > 靜態資源收斂 > 無意義單詞收斂 > 智能收斂> 基數空間收斂
SQL類型
自訂收斂 > SQL規整化 > 基數空間收斂
IP等其他類型
自訂收斂 > IP收斂 > 基數空間收斂
服務端
不適用
收斂執行將按照上述順序逐一執行,維度值被任一收斂機制收斂後,將不再繼續執行後續邏輯。
常見問題
在哪裡可以看到收斂前的原始值?
Trace資料中會記錄原始的值也會記錄收斂後的值,您可以通過調用鏈分析功能查看到原始值。更多資訊,請參見調用鏈分析。
收斂結果不滿足預期,我該如何處理?
可以通過自訂收斂規則能力進行調整。
開啟新版ARMS控制台,切換到應用設定/收斂配置功能,即可添加自訂收斂規則。
樣本:
添加匹配
/api/v1/user/\d+/info
收斂為/api/v1/user/userId/info
,即可將形如/api/v1/user/124343543/info
的請求收斂為/api/v1/user/userId/info
。收斂結果誤傷了一些重要介面,我不想這些重要介面被收斂,該如何處理?
參考問題2,開啟收斂配置功能,在自訂收斂塊添加排除項即可。
樣本:
添加
/api/v1/user/9999/info
,那麼/api/v1/user/9999/info
就不會被收斂為/api/v1/user/userId/info
。探針端收斂與服務端收斂有什麼差異?
探針端收斂即意味著收斂行為發生在探針側,那麼上報到服務端資料就已經是收斂過的,對於服務端來說處理的壓力會大大降低,且可以保證100%的資料準確性。
當探針版本較低時,很多收斂機制是不支援的,一旦出現了發散的情況,只能在服務端進行收斂處理,由於探針端並未進行收斂處理,此時上報到服務端的資料包可能會很大。一方面存在因資料包過大被拒絕導致資料丟失的風險,另一方面受限於服務端的處理機制,經過收斂處理後的資料準確性會存在一定的偏差。因此,強烈建議升級到最新版本的探針。