首頁>技術>

您是否曾經在大型微服務架構上遇到效能問題?今天,我將與您分享我的個人故事,內容涉及我們如何在Wix授權系統中成功地將延遲減少了3倍,並最終獲得了個位數(以毫秒為單位)的一致響應。

一些細節:

我和我的團隊負責Wix中的授權系統。該系統(除其他外)應該能夠回答一個簡單的問題:"是否允許呼叫實體在提供的資源上執行請求的操作?"。這個問題的答案可以是"是"或"否",但是要獲得這個答案可能很困難。我們稱這個問題從現在開始是允許的。

在過去的幾個月中,我們完全重寫了Wix中的授權系統。我們從基於角色的訪問控制(AKA RBAC)切換到基於屬性的訪問控制(ABAC),這為我們提供了可以提供的功能方面更大的靈活性。我們期望新系統更加健壯和高效能,當我們達到功能均等性的重要里程碑時,我們意識到儘管新系統的效能要比舊系統好,但仍然不夠好。

有人可能會說"不夠"是要爭論的事情。在某些情況下,一秒的等待時間就足夠了,而在其他情況下,每納秒很重要。完成重寫後,我們的系統能夠在約30ms內回答。聽起來不錯吧?錯誤。

考慮一個事實,即Wix中的每個端點都從內部和外部呼叫者那裡獲取流量。還應考慮到單個呼叫可以透過多個服務,從而彙總每個服務的資訊。因此,在客戶端請求的單次往返中,期望並接受多個isPermitted呼叫。現在,當對isPermitted的每次呼叫花費大約30ms時,我們可以輕鬆地將每個呼叫的時間提高到數百ms,而無需執行任何其他邏輯。顯然,這個數字不好。我們必須重新考慮我們做出的許多決定,並瞭解如何做得更好。

首先,我們需要定義我們的目標。30毫秒是一個數字,它本身並不能說太多。我們從瞭解總體情況開始。30ms是處理一次平均呼叫所需的時間嗎?這是最壞的情況嗎?這是最好的情況嗎?在解決效能問題時,通常以百分位數來衡量此類問題。您經常會看到p50,p95,p99等字樣。這些都表示測量引數所依據的百分位。p50表示第50個百分位(表示系統對50%的呼叫者的效能如何),p99表示第99個百分位,依此類推。我們在這些引數下測量了端點,這是得到的結果:

· 在p50中,我們能夠在28毫秒內做出響應。

· 在p95–56ms中。

· 在p99–90ms中。

情況變得更加清晰。我們能夠看到問題在第50個百分位數上已經很大,因此我們的目標是首先針對此領域的修復,然後再著手改進更嚴重的問題。

現在我們有了這三個數字,我們仍然對應該改進的地方並不瞭解。我們的下一步是將內部邏輯分解為可度量的單元,以查明問題。有一些工具可以為您做到這一點,但是基本思想是用計時器包裝每個這樣的邏輯單元,在執行程式碼之前啟動它,然後立即停止它。此類邏輯的示例可以是從某些外部資料儲存讀取,對外部服務的呼叫或從檔案的讀取操作。通常(除非您正在編寫具有大量資料的多次迭代的超級複雜的業務邏輯)–大多數時間將花費在各種IO上。

接下來,您應該將結果報告給外部日誌,並且最好將其聚合為一個漂亮的視覺化圖形(我們使用了Grafana)。現在,我們對端點的內部核心有了清晰的瞭解,並且可以看到我們可以進行哪些改進。

重要說明:您應該確保報告此資料也不會影響效能。同步寫入日誌檔案(例如)可能會有其自身的開銷,並且可能導致整個服務的效能更差。確認您使用的工具不會對其產生影響。

> Metrics visualization in Grafana

現在,我們有了這張很棒的圖表,它能夠指出每次更改的實際效果。部署完每個改進後,我們等待了一會兒,然後檢視更新後的圖表以減少所有(或某些)指標。這為我們提供了有關已部署修復程式表現良好的即時反饋。

困難的部分到了-優先順序。一旦掌握了流程的每個部分以及所需的時間,您便擁有了所需的所有資訊,從而可以確定要首先解決和改進的部分。根據我的經驗,做出決定時需要考慮一些因素:

· 它有什麼好處?—您將節省幾秒(在我們的情況下為ms),並且改進措施從總響應時間(百分比)中節省了什麼?

· 您能夠進行哪些改進,以及每項改進有多困難?這需要對程式碼進行一些深入的研究,並瞭解需要花費很長時間以及如何改進它。我將在本文中進一步分享一些常見的用例,但是每個業務領域可能都有自己的調整。您需要能夠(或多或少)宣告—每個流程中的痛點在哪裡,以及如何使它們減輕傷害。

· 外部依賴關係-某些改進可能需要組織中的其他團隊參與(可能是系統或框架中的更改,可能是您使用的外部服務執行得太慢)。您需要考慮到這一點。如果您的端點需要5秒鐘,其中有4秒鐘是花在等待某些服務響應上的-您實際上沒有什麼事要做(但外部服務的所有者可能要做)。

· 向後合規-重大更改很有害。他們傷害了您(因為您需要確保所有客戶都已遷移),並且傷害了您的客戶(因為現在他們還有另一項本來不打算執行的任務)。例如,在提高效能時,您可能希望從呼叫方獲取一些額外的引數。通常,最好不要強迫您的客戶端傳遞這些引數,而是宣告每個這樣做的人都會提高效能。這將推動最受苦的客戶做出您需要他們進行的更改。

在考慮了所有這些問題之後,我的建議是首先從快速獲勝開始。您可能會有很小的變化,這不會使世界完全顛覆,但是您將減少執行時間幾毫秒。這些快速的勝利相互疊加,最終以很小的代價獲得了很大的進步。

要記住的另一件事是您的長期策略。請勿進行與最終目標不符的更改,因為最終您將不得不還原它們。就我們而言,根據定義,ABAC世界中的某些事情更加複雜。我們本來可以簡化它們,但反過來會失去ABAC提供的某些功能-但請記住,我們要達到的最終目標迫使我們不要簡化它。

擴大

為了獲得更好的效能,您首先要考慮的是擴充套件系統。可能會有更多的例項,更強大的例項,更多的記憶體等。這是一個很好的指示,那就是您的服務大多數時候都在較高的CPU上執行,浪費大量時間在等待空閒執行緒來處理請求或與資料庫的免費連線(在這種情況下,您可能需要考慮擴充套件資料庫而不是應用程式)。如果您在邏輯上遇到實際問題,而不是缺乏資源,這可能無濟於事,但是,這是您可能獲得的第一個(也是輕鬆的)勝利。就我們而言,我們缺乏記憶。我們相應地對其進行了擴充套件,最後進行了微小但必不可少的改進。

從WRITE中拆分資料庫讀取

如果您管理一項接收大量流量的服務,則可能有多個數據庫副本。我們就是這種情況-我們的服務在每個資料中心的主從架構中都有多個數據庫副本。我們的配置指出,可以從最近的副本執行讀取操作,但是寫入操作始終會命中主DC中的主DB。

我們做的第一件事(或更準確地說,完成了,因為已經完成了一部分)是確保所有讀取都到達了輔助資料庫。這會產生巨大的影響,尤其是在較高的百分位數上。考慮一下這一點:跨DC呼叫(即對非常遠端的例項的呼叫)導致100ms的損失(例如)。這將導致DB讀取一直傳到主DC,而不是在非常靠近的計算機中進行超快速操作。該解決方案的另一個不利方面是,主資料庫將被載入很多讀操作,而不必這樣做。這可能會導致速度變慢(您必須等待免費連線),在最壞的情況下甚至會導致停機!在將讀取次數與寫入次數分開之後,我們看到減少了多達16%。我們開始取得進步!

您認為記憶體中的所有內容實際上是嗎?

我們看到的第二件事是,我們認為該操作非常快,每次呼叫花費了大約2ms的時間。在此程式碼流程中,我們已完成7至15次此操作。該操作正在讀取特定的標頭。我們不知道的是,此標頭以序列化的方式儲存在那裡。每次我們想閱讀它時,都必須對它進行反序列化處理,然後才能使用我們得到的物件。在實施第一個解決方案時,我們根本沒有考慮到這一點,如果沒有我們在較早階段實施的指標,我們將一無所知!

在這一點上,解決方案是"容易的"。我們要做的就是對它進行反序列化一次,然後將其傳遞給底層。從理論上講,這聽起來很簡單,但實際上,我們知道將變數滲透到方法中可能是一個麻煩的過程,尤其是在涉及測試和模擬的情況下。此類更改應在手術集中進行,因為立即更改生產程式碼和測試非常危險。在這裡要學習的教訓是,除了給定的引數外,內部元件最好不要依賴任何外部元件。所有外部讀取都應在業務邏輯外部進行設定,無論環境如何,我們都希望對同一輸入始終進行相同的操作。這是我們必須學習的艱難方法。完成此更改後,我們發現延遲顯著減少了32%。

殺死代理

作為重寫的一部分,我們保留了舊的授權系統(已替換的授權系統)作為新系統的代理。我們這樣做是為了避免讓所有客戶端將其讀取切換到新系統,以便能夠控制請求流,並在需要時回退到舊系統。我們注意到的一件事是,透過代理進行呼叫時,請求的延遲增加了。這是由於代理必須進行一次額外的呼叫,並且在嚮往返新增另一跳時必須支付的罰款。

我們所做的下一個改進是殺死該代理。就像我說的那樣,破壞性更改很有害,因此為了不破壞任何人,我們建議客戶遷移到他們的身邊以提高效能。這將球帶到了客戶的領域-感到延遲之痛的人立即遷移。其他人將在有組織的棄用過程中一點一點地遷移。請記住,通往答案的每一跳都有代價,並嘗試儘可能減少這些跳數。

請注意,並不是您途中的每項服務都需要被殺死,並且其邏輯應該被移動。微服務架構非常重要,它比大規模的整體元件具有更大的可擴充套件性,並且對速度產生巨大影響。終止服務幾乎總是要付出代價的(這可能不是立即的)。

快取

授權世界比較棘手。您永遠不想讓任何人一方面做他們不允許做的事情,但是另一方面,您遇到的大多數問題都是您已經多次回答的問題。我們看到,對於該過程中的大多數呼叫,我們一遍又一遍地檢索相同的策略。

這就是我們決定快取此類通用策略的重點。快取並不是解決問題的靈丹妙藥,正如Phil Karlton曾經說過的那樣:"計算機科學中只有兩件事:快取無效和命名。"但是,在這種情況下,我們有明確的跡象表明,取消往返資料庫的往返(或更確切地說,每隔一段時間執行一次)將使我們的普通客戶受益匪淺。

結論

我們從一個電話號碼(呼叫的響應時間)開始,然後說這太慢了。在此過程中,我們逐步引入了許多指標(對生產監控也非常有幫助),並逐步改進了系統的未最佳化部分。正如您在下圖中所看到的,我們最終獲得了令人驚訝的結果,並且汲取了很多教訓。

這是我從這個專案中獲得的收益:

· 在深入探討並得出結論之前,請盡一切可能。

· 優先考慮哪些將給您帶來最大的好處,而哪些則將使您花費最多(金錢或開發時間)。

· 如果需要,可擴大資源。

· 拆分資料庫從寫入讀取。

· 重新檢視您對系統各部分的工作速度所做的假設。

· 知道請求必須跳多少跳,並儘可能減少跳數。

· 考慮將快取作為一種最佳化。

我們的旅程還沒有結束

我們正在考慮採用多種解決方案來不斷提高效能,因此這只是我們故事的第一步。希望不久以後我還能再發表一個標題相同的帖子!

13
最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • k8s學習之儲存卷volume詳解