服務端基本概念和指標

喵耳朵發表於2021-12-16

這裡主要介紹服務端架構工作中的一些常見的概念和指標,在我們部署、上線等運維工作時,方便排查問題,以及交流時的語義一致。

上游和下游 upstream and downstream

一般在談論服務和呼叫關係的時候,我們會使用上游和下游來表示服務間的相關依賴。但是對於上下游的定義,會視情況而定的。

Stack Overflow上有個相同的問題:definition - Upstream / downstream terminology used backwards? (E.g. nginx) - Stack Overflow

以下是點贊最多的回答的節選:

Formal definition was added later, in RFC 2616:

upstream/downstream

Upstream and downstream describe the flow of a message: all messages flow from upstream to downstream.

According to this definition:

  • if you are looking at a request, then the client is upstream, and the server is downstream;
  • in contrast, if you are looking at a response, then the client is downstream, and the server is upstream.

中文意思是:

  1. 如果是從請求的角度去看,那麼客戶端是服務端的上游,服務端是客戶端的下游;
  2. 如果是從返回資料的角度來看,那麼資料是從服務端返回給了客戶端,因此服務端是客戶端的上游,客戶端是服務端的下游。

在本文中,我們強調的是服務之間的呼叫關係,因此是以“請求”的角度來看到,即按照客戶端為上游服務端為下游的說法。

另外,在討論A服務將資料寫入一個訊息佇列,B服務消費訊息佇列的資訊的這種情況,我們是按照資料的流向來定義上下游,此時,A為上游,B為下游。

扇入和扇出 fan-in and fan-out

扇入/扇出,在百度百科中有比較好的定義:扇出能力_百度百科 (baidu.com)

軟體設計中,扇入和扇出的概念是指應用程式模組之間的層次呼叫情況。

按照結構化設計方法,一個應用程式是由多個功能相對獨立的模組所組成。

扇入:是指直接呼叫該模組的上級模組的個數。扇入大表示模組的複用程度高。

扇出:是指該模組直接呼叫的下級模組的個數。扇出大表示模組的複雜度高,需要控制和協調過多的下級模組;但扇出過小(例如總是1)也不好。扇出過大一般是因為缺乏中間層次,應該適當增加中間層次的模組。扇出太小時可以把下級模組進一步分解成若干個子功能模組,或者合併到它的上級模組中去。

設計良好的軟體結構,通常頂層扇出比較大,中間扇出小,底層模組則有大扇入

服務效能指標

在觀察一個服務時,我們可以從多種角度去判定。

CPU負載 & 記憶體佔用

如果服務的響應變慢,延時增高,我們優先需要檢視機器的負載情況。

這兩個比較簡單,使用常用的top命令就可以看到。另外使用top -H可以看到執行緒級別的佔用。以下是top的結果。

linux-top

這裡紅色的框表示這個程式的CPU的佔用,這個數字表示佔用了多少CPU核。在4核的機器上,單個程式的佔用可以達到300%+。

藍色的框表示記憶體佔用,這裡是該程式對整個機器的記憶體的佔用情況。

順便介紹一下每一列的含義:

  • PID: 程式編號
  • USER: 啟動該程式的使用者名稱
  • PR: 程式優先順序
  • NI: nice值,越低表示優先順序越高
  • VIRT: virtual memory,程式使用的虛擬記憶體總量,單位:kb。VIRT=SWAP+RES
  • RES: 程式使用的、未被換出的實體記憶體大小,單位:kb。RES=CODE+DATA
  • SHR: 共享記憶體大小,單位:kb
  • S 程式狀態
    • D: 不可中斷的睡眠狀態
    • R: 執行
    • S: 睡眠
    • T: 跟蹤 / 停止
    • Z: 殭屍程式
  • %CPU: 上次更新到現在的 CPU 時間佔用百分比,100%表示用滿單核的資源,4核機器理論上限400%
  • %MEM: 記憶體佔用百分比
  • TIME+: 程式使用的 CPU 時間總計,精確到 1/100 秒
  • COMMAND: 命令名 / 命令列

接下來介紹一下我遇見過的一些案例:

  1. 服務升級後,延時穩定上漲2ms

這種情況下,一般先對比上線前後的CPU佔用,發現新版服務的CPU佔用上漲(一般服務都有自己的狀態看板)。之後檢視服務更新的程式碼,確定是有新增的功能。

此時,需要評估新增功能對資源的消耗是否符合預期。不符合,則考慮回滾,符合則保持。

  1. 服務重啟,延時上漲,一段時間後恢復

這種情況,一般是服務自身記憶體有Cache資料,服務重啟之後所有的Cache情況,此時對於所有的請求,都必須重新請求一次下游。資料的獲取和解析需要額外的時間和CPU,以及記憶體的申請,因此延時上漲。一段時間後,Cache的命中率和重啟前一致,延時正常。

這裡需要注意的是,服務的Cache清空,導致對下游的請求裡一次性增大,有可能會造成下游的雪崩問題。因此一方面下游需要做好應對策略,另一方面,上游的服務也儘量保持小粒度的滾動升級,不要一次性全部升級。

頻寬

這篇文章有比較詳細的介紹:[656]linux檢視伺服器頻寬_周小董-CSDN部落格_linux 檢視頻寬

通過nload工具(機器不存在的話,可以apt install安裝)可以比較方便的檢視網路卡的流入流出的流量。

一般對於存放資料的服務,流量大的時候會存在頻寬打滿的情況。此時服務的CPU可能負載並不高,但是上游的延時或者錯誤率上漲。這裡的頻寬包括了單機、交換機等的頻寬。

這裡我也見過幾次頻寬導致的問題。

主要的現象是,客戶端的呼叫時間遠大於服務端的內部時間。這基本上可以認為是網路傳輸的問題。有兩種可能:

  1. 頻寬打滿。這種情況只能找運維了。
  2. 跨機房。一般為了優化服務呼叫,會在一個機房部署整套的相關服務,這裡RPC呼叫會優先在同機房內進行。在容災或者其他場景下,有可能有跨機房的需求,這樣延時勢必會上漲。

介面指標

QPS/TPS

  • QPS (Queries Per Second),每秒查詢率。表示服務每秒能夠相應的查詢次數,是對一個特定的查詢伺服器在規定時間內所處理流量多少的衡量標準。
  • TPS(Transactions Per Second),事務數/秒。它是軟體測試結果的測量單位。一個事務是指一個客戶機向伺服器傳送請求然後伺服器做出反應的過程。客戶機在傳送請求時開始計時,收到伺服器響應後結束計時,以此來計算使用的時間和完成的事務個數。

上面的概念是照搬的其他的部落格的說法。我在日常工作中,基本上沒有遇到過TPS的概念。

對於QPS,由於每個服務都是以叢集的方式部署,所以QPS分為整體QPS單機QPS。我們可以粗略的認為整體QPS=單機QPS x 機器數。但實際上,由於一致性Hash等策略,每臺機器的負載可能不均,這是一些問題排查的干擾項,我們在後續的一致性Hash和Shard的章節具體介紹。

併發數

併發數,系統同時能夠處理的請求/事務數量。注意,這裡只是處理,並不是處理完。

響應時間 & Latency

響應時間即完成一次事務所需要的時間。一般從AVG、PCT90、PCT95、PCT99(PCT99可以簡稱P99)等多個維度去看。有時候也叫Latency。

需要注意的是,時延有服務端和客戶端兩個視角。一般可以理解: 客戶端時延 = 服務端時延 + 網路時延

  • AVG: Average,表示平均Latency
  • PCT90: 將延時由小到大排列,90%的位置的時延。即90%的請求可以在XXX時間內得到響應
  • PCT99: 99%的請求可以在XXX時間內得到響應

一般其實我其實主要看AVG和PCT99兩個指標。一般新上線功能的時候,AVG和P99不應該有大的變化。如果有,則需要分析是否符合預期。

線上上如果出現報警,也可以觀察這兩個指標。

客戶端時延 > 服務端時延

網路傳輸需要時間,所以肯定有差異。如果差異較大,考慮是否是資料量太大、頻寬不足或跨機房。

客戶端時延 < 服務端時延

客戶端設定了超時,提前結束的響應。而服務端繼續執行。浪費算力。

AVG變化小 PCT99變化小

正常狀態。也可以和歷史的時延對照來看。

AVG變化小 PCT99變化大

這種情況,一般是單機/小叢集的問題。1%的機器的延時上漲就會影響P99的變化,但對AVG影響較小。

因此可能是小流量的實驗或者機器故障。

AVG變化大 PCT99變化小

這種有點反直覺。
一種可能是本身服務超時嚴重,pct99就等於設定的超時時間。
或者服務端拒絕響應,被拒絕的請求的延時就基本上等於0。而觀察的時候沒有區分是成功或是失敗的響應,就可能出現這種現象。

AVG變化大 PCT99變化大

全量服務變更或者故障,建議高優排查。

錯誤率 & 狀態碼

顧名思義,表示請求錯誤的比例。這裡的錯誤有兩種: 框架錯誤和業務錯誤。

  1. 框架錯誤,包括服務故障或壓力導致的無法訪問。例如連線超時、服務地址不存在、rpc method沒對齊等錯誤。
  2. 業務錯誤,服務本身狀態良好,但由於請求缺少相關欄位,或者服務的下游故障等,導致該服務無法獲取到正確結果。這時,需要服務返回業務的狀態碼。

一般框架協議會提供一套錯誤碼機制,比如http的狀態碼,200表示success等。
同時在框架狀態碼為success時,介面一般會返回業務狀態碼,表示業務的執行情況。比如“使用者未登入”、“token無效”、“success”等。
通過精細化狀態碼的含義,可以有效的協助我們有區分性地排查架構和業務問題。

日誌、打點、埋點

日誌

一般就是我們程式執行輸出的文字資訊。最簡單的日誌系統就是把日誌檔案存在在服務所在的機器上。需要排查問題的時候登入到對應的機器,檢視日誌檔案。
常見的有很多日誌庫,spdloglog4cpp等。在寫本文的時候,剛好爆出了log4j這個日誌庫的命令注入的bug。

一般一條日誌就是一行文字。包含了日期,級別,日誌所在檔案和行號,具體資訊等。可以通過配置來定製。日誌級別常見的有Error, Warning, Info, Debug, Trace等。
也有公司會將日誌寫入ElasticSearch等引擎,便於問題排查和回溯。

打點

打點也是日誌的一種,不同於常規日誌的簡單的記錄功能。打點的資料會被彙總並做聚合分析,常見的打點型別有計數counter,記時timer和存值store。以及tag,便於按條件過濾。

  • 計數可以統計qps等。
  • 記時可以分析服務響應時間等,比如前面的pct99等指標。
  • 存值一般就寫入當前記憶體數,cpu使用數,快取大小等,方便分析服務狀態。

打點系統一般包括資料採集、聚合、查詢、視覺化等部分。
相關的開源工具有Prometheus,grafana等,這裡我也瞭解不多。

埋點

埋點一般指網頁或者客戶端的打點日誌。比如使用者點選了一個視訊,在某個新聞停留多長時間,下載了app,充值,點選購物車或者購買商品等。
客戶端埋點資料會實時或者定時的上傳。(部分資料可能需要廣告主等回傳)
一般一次完整的互動過程會有一個唯一的trace_id,每一個環節的日誌都包含了trace_id。因此通過trace_id對服務端和客戶端的日誌進行關聯,我們就可以還原出一次互動的完整生命週期。
公司可以利用這些資料做使用者畫像,訓練模型,提供更個性化的服務。

更可怕的是,不同的公司會共享這部分資料。比如當在微信搜尋某件商品後,開啟京東,會給你推薦相關的產品。可見大資料無處不在。還好,相關的政策在不斷的提出,相信資訊濫用的情況會逐漸好轉。

其他的概念

這裡有一些服務相關的概念,我平時接觸的也很少。僅記錄一下。

降級-熔斷-限流

關於降級,熔斷,限流,知乎的這篇文章有比較好的介紹。降級-熔斷-限流-傻傻分不清楚 - 知乎 (zhihu.com)

反壓

這個概念主要出現在資料流的任務中。比如A服務將資料寫入訊息佇列,B服務消費訊息佇列。如果B服務處理的過慢,會導致A服務必須降速或停止寫入,否則訊息佇列會擠壓大量的任務。
一般情況下B的處理效率會比A高的,因此訊息佇列一直很空。但當B出現GC,或者依賴的下游抖動時,B的處理效率可能會短時變差,從而對A造成反壓,處理不當的話,會反壓更上游的服務。

我見到的反壓一般都是短時的,通過重啟就可以較好的緩解。這裡需要保障重啟之後的資料狀態可以恢復,或者丟棄的任務不影響整體的效果。
如果重啟無效,還可以考慮上游做流量降級。甚至直接丟棄當前佇列的資料。
故障是隨時都會發生的,我們能做的無非是未雨綢繆和擇禍從輕。

eBPF

eBPF是一種讓核心可程式設計的技術。
https://zhuanlan.zhihu.com/p/182344856

比如實時監控主機的記憶體、CPU、頻寬等功能均可以通過該技術來實現。

這裡為什麼專門記錄一下呢,是因為之前有線上問題最終排查是主機上的一個eBPF程式裡維護了一個低效能的map結構,佔用大量CPU。因此有時候服務的效能並不完全由自己的硬體和程式決定。還存在不知道哪裡來的第三方。

綁核

這個一般是指設定程式的CPU親和度。

比如某些嵌入式裝置存在不同規格的CPU,在測試效能時可能會出現多次測試的結果差異很大的情況,大概率是執行程式的核心差異導致的。一般使用 taskset 命令。

在服務部署也有類似的問題,目前使用k8s進行服務部署,單個物理機會同時部署多個例項。通過綁核的操作,可以使程式(儘量)在固定的CPU執行,這樣在NUMA架構下會更加友好。

寫在後面

本文介紹了許多我們談論架構時常見的名詞,以及比較簡單的案例和分析。

由於概念的歸類難以統一,因此本文的組織結構也並不嚴謹。每種專業術語的定義雖然也在網上查了下,仍會存在不準確的情況。
在我的日常工作,暫時還沒有因為術語的不對齊而導致的誤解,相信本文的介紹總體上還算正確,有問題請及時指正。

至此,本文就結束了。如果想到新的有意思的概念,我會繼續在這裡補充。

期待早日完成下一篇部落格。

相關文章