Yandex.Market 搜尋的工作原理以及其中一台伺服器發生故障時會發生什麼

你好,我叫葉夫根尼。 我在 Yandex.Market 搜尋基礎設施中工作。 我想告訴哈布爾社區關於市場內部廚房的資訊 - 我有很多話要說。 首先,市場搜尋的工作原理、流程和架構。 我們如何處理緊急情況:如果一台伺服器發生故障怎麼辦? 如果有 100 個這樣的伺服器怎麼辦?

您還將了解我們如何同時在一堆伺服器上實現新功能。 以及我們如何直接在生產中測試複雜的服務,而不會對用戶造成任何不便。 總的來說,市場搜尋如何運作,讓每個人都玩得開心。

Yandex.Market 搜尋的工作原理以及其中一台伺服器發生故障時會發生什麼

關於我們的一些資訊:我們解決什麼問題

當您輸入文字、按參數搜尋產品或比較不同商店的價格時,所有請求都會傳送到搜尋服務。 搜尋是市場上最大的服務。

我們處理所有搜尋請求:來自 market.yandex.ru、beru.ru、Supercheck 服務、Yandex.Advisor、行動應用程式等網站。 我們也在 yandex.ru 的搜尋結果中包含產品報價。

Yandex.Market 搜尋的工作原理以及其中一台伺服器發生故障時會發生什麼

我所說的搜尋服務不僅指搜尋本身,還指包含市場上所有報價的資料庫。 規模是這樣的:每天處理超過十億個搜尋請求。 一切都應該快速進行,不間斷,並且始終產生預期的結果。

什麼是:市場架構

我將簡要描述市場目前的架構。 可以用下圖來大致描述:
Yandex.Market 搜尋的工作原理以及其中一台伺服器發生故障時會發生什麼
假設有一家合作夥伴商店來找我們。 他說我想賣玩具:這隻邪惡的貓,會發出吱吱聲。 還有另一隻憤怒的貓,沒有吱吱聲。 而且只是一隻貓。 然後商店需要準備市場搜尋的報價。 商店產生包含優惠的特殊 xml,並透過聯盟介面傳達此 xml 的路徑。 然後,索引器定期下載此 xml,檢查錯誤並將所有資訊保存到一個巨大的資料庫中。

這樣保存的xml很多。 從此資料庫建立搜尋索引。 索引以內部格式儲存。 建立索引後,佈局服務將其上傳到搜尋伺服器。

結果,資料庫中出現了一隻發出吱吱聲的憤怒的貓,而該貓的索引出現在伺服器上。

我會在搜尋架構部分告訴你我們如何搜尋一隻貓。

市場搜尋架構

我們生活在一個微服務的世界:每個傳入的請求 market.yandex.ru 會產生大量的子查詢,並且涉及數十個服務的處理。 圖中僅顯示了一些:

Yandex.Market 搜尋的工作原理以及其中一台伺服器發生故障時會發生什麼
簡化的請求處理方案

每個服務都有一個奇妙的東西 - 它自己的平衡器具有唯一的名稱:

Yandex.Market 搜尋的工作原理以及其中一台伺服器發生故障時會發生什麼

平衡器為我們管理服務提供了更大的靈活性:例如,您可以關閉伺服器,這通常是更新所必需的。 平衡器發現伺服器不可用,會自動將請求重新導向到其他伺服器或資料中心。 新增或刪除伺服器時,負載會自動在伺服器之間重新分配。

平衡器的唯一名稱不依賴資料中心。 當服務 A 向 B 發出請求時,預設均衡器 B 會將請求重新導向到目前資料中心。 如果該服務不可用或目前資料中心不存在,則請求將被重新導向到其他資料中心。

所有資料中心的單一 FQDN 允許服務 A 完全從位置中抽象化。 他對服務 B 的請求將始終得到處理。 例外情況是服務位於所有資料中心的情況。

但這個平衡器並不是一切都那麼美好:我們還有一個額外的中間組件。 平衡器可能不穩定,這個問題可以透過冗餘伺服器來解決。 服務 A 和 B 之間還有額外的延遲。但實際上,該延遲小於 1 毫秒,對於大多數服務而言,這並不重要。

處理意外情況:搜尋服務平衡與彈性

想像崩潰:你需要找到一隻發出吱吱聲的貓,但服務器崩潰了。 或 100 台伺服器。 怎麼出去? 我們真的要讓用戶沒有貓嗎?

情況很可怕,但我們已做好準備。 我按順序告訴你。

搜尋基礎設施位於多個資料中心:

Yandex.Market 搜尋的工作原理以及其中一台伺服器發生故障時會發生什麼

在設計時,我們考慮了關閉一個資料中心的可能性。 生活充滿了驚喜 - 例如,挖掘機可以切斷地下電纜(是的,確實發生過)。 其餘資料中心的容量應足以承受尖峰負載。

讓我們考慮單一資料中心。 每個資料中心都有相同的平衡器運作方案:

Yandex.Market 搜尋的工作原理以及其中一台伺服器發生故障時會發生什麼
一台平衡器至少是三台實體伺服器。 這種冗餘是為了可靠性。 平衡器在 HAProx 上運作。

我們選擇 HAProx 是因為它具有高效能、低資源需求和廣泛的功能。 我們的搜尋軟體在每台伺服器內運作。

一台伺服器發生故障的可能性很低。 但如果您有很多伺服器,那麼至少一台伺服器發生故障的可能性就會增加。

這就是現實中發生的情況:伺服器崩潰。 因此,有必要不斷監控所有伺服器的狀態。 如果伺服器停止回應,它會自動斷開流量。 為此,HAProxy 具有內建的健康檢查。 它每秒向所有伺服器發送一次 HTTP 請求“/ping”。

HAProxy 的另一個功能:代理程式檢查可讓您均勻地載入所有伺服器。 為此,HAProxy 連接到所有伺服器,它們根據當前負載(從 1 到 100)返回其權重。權重是根據佇列中待處理的請求數和處理器上的負載計算的。

現在關於尋找貓。 搜尋結果的請求如下: /搜尋?文字=憤怒+貓。 為了使搜尋速度更快,整個 cat 索引必須適合 RAM。 即使從 SSD 讀取也不夠快。

曾幾何時,報價資料庫很小,一台伺服器的 RAM 就足夠了。 隨著報價基礎的成長,所有內容都不再適合此 RAM,資料被分為兩部分:分片 1 和分片 2。

Yandex.Market 搜尋的工作原理以及其中一台伺服器發生故障時會發生什麼
但這種情況總是會發生:任何解決方案,即使是一個好的解決方案,也會造成其他問題。

平衡器仍然會轉到任何伺服器。 但在請求來的機器上,索引只有一半。 其餘的在其他伺服器上。 因此,伺服器必須轉到某些相鄰的電腦。 從兩台伺服器接收資料後,將結果合併並重新排名。

由於平衡器均勻分配請求,因此所有伺服器都參與重新排名,而不僅僅是發送資料。

如果相鄰伺服器不可用,就會出現此問題。 解決方案是將多個具有不同優先權的伺服器指定為「相鄰」伺服器。 首先,請求被傳送到目前機架中的伺服器。 如果沒有回應,則請求將發送到該資料中心的所有伺服器。 最後,該請求發送至其他資料中心。
隨著提案數量的增加,數據被分成四個部分。 但這還不是極限。

目前,使用八個分片的配置。 另外,為了節省更多內存,索引被分成搜尋部分(用於搜尋)和片段部分(不參與搜尋)。

一台伺服器僅包含一個分片的資訊。 因此,要搜尋全索引,需要在八個包含不同分片的伺服器上搜尋。

伺服器被分組為叢集。 每個叢集包含八個搜尋引擎和一個程式碼片段伺服器。

Yandex.Market 搜尋的工作原理以及其中一台伺服器發生故障時會發生什麼
程式碼片段伺服器運行帶有靜態資料的鍵值資料庫。 他們需要發布文件,例如對一隻發出吱吱聲的貓的描述。 資料專門傳輸到單獨的伺服器,以免載入搜尋伺服器的記憶體。

由於文件 ID 僅在一個索引內是唯一的,因此可能會出現片段中沒有文件的情況。 嗯,或者說一個ID會有不同的內容。 因此,為了使搜尋正常工作並傳回結果,整個叢集需要保持一致性。 下面我將告訴您我們如何監控一致性。

搜尋本身的結構如下:搜尋請求可以到達八個伺服器中的任何一個。 假設他來到伺服器 1。該伺服器處理所有參數並了解要尋找的內容和方式。 根據傳入的請求,伺服器可以向外部服務發出額外的請求以獲取必要的資訊。 一個請求後最多可以有十個對外部服務的請求。

收集必要的資訊後,開始在報價資料庫中搜尋。 為此,需要對叢集中的所有八台伺服器進行子查詢。

一旦收到回應,結果就會合併。 最後,可能需要對程式碼片段伺服器進行更多子查詢才能產生結果。

叢集內的搜尋查詢如下所示: /shard1?text=憤怒+貓。 此外,叢集內的所有伺服器之間每秒不斷地進行一次以下形式的子查詢: /地位.

詢問 /地位 檢測到伺服器不可用的情況。

它還控制所有伺服器上的搜尋引擎版本和索引版本相同,否則會出現叢集內資料不一致的情況。

儘管一個片段伺服器處理來自八個搜尋引擎的請求,但其處理器的負載非常輕。 因此,我們現在將片段資料傳輸到單獨的服務。

Yandex.Market 搜尋的工作原理以及其中一台伺服器發生故障時會發生什麼

為了傳輸數據,我們引入了文檔通用密鑰。 現在不可能出現用一個鍵傳回另一個文件的內容的情況。

但向另一種架構的過渡尚未完成。 現在我們想擺脫專用的片段伺服器。 然後完全脫離集群結構。 這將使我們能夠繼續輕鬆擴展。 額外的好處是可以節省大量的鐵。

現在來談談結局美好的恐怖故事。 讓我們考慮幾種伺服器不可用的情況。

發生了可怕的事情:一台伺服器不可用

假設一台伺服器不可用。 那麼叢集中剩餘的伺服器可以繼續回應,但搜尋結果將不完整。

通過狀態檢查 /地位 相鄰伺服器知道其中一台伺服器不可用。 因此,為了保持完整性,叢集中的所有伺服器每個請求 /平 他們開始向平衡器做出回應,說他們也不可用。 事實證明,叢集中的所有伺服器都死掉了(這不是事實)。 這是我們叢集方案的主要缺點 - 這就是我們想要擺脫它的原因。

Yandex.Market 搜尋的工作原理以及其中一台伺服器發生故障時會發生什麼

因錯誤而失敗的請求將由其他伺服器上的平衡器重新發送。
平衡器也會停止向失效伺服器發送使用者流量,但會繼續檢查其狀態。

當伺服器可用時,它開始回應 /平。 一旦來自失效伺服器的 ping 的正常回應開始到達,平衡器就開始向那裡發送用戶流量。 叢集運作已恢復,萬歲。

更糟的是:許多伺服器不可用

資料中心的很大一部分伺服器被削減。 做什麼,往哪裡跑? 平衡器再次來救援。 每個平衡器不斷在記憶體中儲存當前活動伺服器的數量。 它不斷計算目前資料中心可以處理的最大流量。

當資料中心中的許多伺服器發生故障時,平衡器意識到該資料中心無法處理所有流量。

然後多餘的流量開始隨機分配到其他資料中心。 一切順利,每個人都很高興。

Yandex.Market 搜尋的工作原理以及其中一台伺服器發生故障時會發生什麼

我們如何做:發布版本

現在我們來談談如何發布對服務所做的更改。 在這裡,我們採取了簡化流程的方法:推出新版本幾乎完全自動化。
當專案中累積一定數量的變更時,會自動建立新版本並開始建置。

Yandex.Market 搜尋的工作原理以及其中一台伺服器發生故障時會發生什麼

然後將該服務推出進行測試,檢查操作的穩定性。

同時啟動自動性能測試。 這是由特殊服務處理的。 我現在不談它——它的描述值得單獨寫一篇文章。

如果測試發布成功,將自動開始在 prestable 中發布該版本。 Prestable 是一個特殊的集群,引導正常用戶流量。 如果回傳錯誤,平衡器會向生產重新發出請求。

在 prestable 中,響應時間被測量並與生產中的先前版本進行比較。 如果一切正常,則有人進行連接:檢查負載測試的圖表和結果,然後開始投入生產。

祝用戶一切順利:A/B 測試

服務的改變是否會帶來真正的好處並不總是顯而易見的。 為了衡量變更的有用性,人們提出了 A/B 測試。 我將向您介紹它在 Yandex.Market 搜尋中的工作原理。

這一切都始於新增啟用新功能的新 CGI 參數。 讓我們的參數為: 市場新功能=1。 然後在程式碼中,如果存在該標誌,我們將啟用此功能:

If (cgi.experiments.market_new_functionality) {
// enable new functionality
}

新功能正在投入生產。

為了自動化 A/B 測試,有一個專門的服務提供詳細信息 此處描述。 在服務中創建了一個實驗。 流量份額設定為例如15%。 百分比不是為查詢設定的,而是為使用者設定的。 也指出了實驗的持續時間,例如一周。

可以同時運行多個實驗。 在設定中,您可以指定是否可以與其他實驗交叉。

結果,服務自動新增一個參數 市場新功能=1 到 15% 的用戶。 它還會自動計算選定的指標。 實驗完成後,分析人員查看結果並得出結論。 根據調查結果,決定投入生產或改進。

市場的靈巧之手:生產中的測試

經常發生這樣的情況:您需要在生產中測試新功能的運行情況,但您不確定它在重負載下的「戰鬥」條件下會如何表現。

有一個解決方案:CGI 參數中的標誌不僅可以用於 A/B 測試,還可以用於測試新功能。

我們製作了一個工具,讓您可以立即更改數千台伺服器上的配置,而不會使服務面臨風險。 這就是所謂的「停止點擊」。 最初的想法是能夠在沒有佈局的情況下快速停用某些功能。 然後該工具不斷擴展並變得更加複雜。

服務流程圖如下:

Yandex.Market 搜尋的工作原理以及其中一台伺服器發生故障時會發生什麼

標誌值透過 API 設定。 管理服務將這些值儲存在資料庫中。 所有伺服器每十秒存取一次資料庫,提取標誌值並將這些值應用於每個請求。

在停止點擊中,您可以設定兩種類型的值:

1) 條件表達式。 當其中一個值為 true 時套用。 例如:

{
	"condition":"IS_DC1",
	"value":"3",
}, 
{
	"condition": "CLUSTER==2 and IS_BERU", 
	"value": "4!" 
}

當在位置 DC3 處理請求時,將套用值「1」。 當在 beru.ru 站點的第二個叢集上處理請求時,該值為「4」。

2)無條件的價值觀。 如果不滿足任何條件,則預設應用。 例如:

價值,價值!

如果某個值以感嘆號結尾,則該值具有較高的優先權。

CGI參數解析器解析URL。 然後套用 Stop Tap 中的值。

應用具有以下優先權的值:

  1. 透過停止點擊(感嘆號)提高優先順序。
  2. 來自請求的值。
  3. 停止點擊的預設值。
  4. 代碼中的預設值。

條件值中指示了許多標誌 - 它們足以滿足我們已知的所有場景:

  • 數據中心。
  • 環境:生產、測試、影子。
  • 地點:貝魯市場。
  • 簇號。

使用此工具,您可以在一組特定伺服器(例如,僅在一個資料中心)上啟用新功能並測試此功能的運行,而不會對整個服務帶來任何特定風險。 即使你在某個地方犯了嚴重的錯誤,一切都開始崩潰,整個資料中心都癱瘓了,平衡器也會將請求重定向到其他資料中心。 最終用戶不會注意到任何事情。

如果您發現問題,可以立即將標誌返回到先前的值,並且變更將回滾。

這項服務也有其缺點:開發人員非常喜歡它,並且經常嘗試將所有變更推送到 Stop Tap 中。 我們正在努力打擊濫用。

當您已經擁有準備好投入生產的穩定程式碼時,Stop Tap 方法效果很好。 同時,你仍然心存疑慮,想要在「戰鬥」條件下檢查代碼。

但是,Stop Tap 不適合在開發期間進行測試。 開發人員有一個單獨的集群,稱為「影子集群」。

秘密測試:影子集群

來自集群之一的請求將被複製到影子集群。 但平衡器完全忽略來自該集群的響應。 其操作圖如下所示。

Yandex.Market 搜尋的工作原理以及其中一台伺服器發生故障時會發生什麼

我們得到了一個處於真實「戰鬥」條件下的測試集群。 正常的用戶流量都去那裡。 兩個叢集中的硬體相同,因此可以比較效能和錯誤。

由於平衡器完全忽略回應,因此最終用戶將看不到來自影子集群的回應。 因此,犯錯並不可怕。

發現

那麼,我們要如何建構市場搜尋呢?

為了使一切順利進行,我們將功能分為單獨的服務。 這樣我們就可以只擴展我們需要的那些元件並使元件更簡單。 將單獨的組件分配給另一個團隊並分擔處理該組件的責任很容易。 透過這種方法可以顯著節省鐵的使用,這是一個明顯的優勢。

影子集群也幫助我們:我們可以開發服務,在過程中測試它們,而不打擾用戶。

嗯,當然是在生產上進行測試。 需要更改數千台伺服器上的配置? 很簡單,使用「停止水龍頭」。 這樣,您可以立即推導出成的複雜解決方案,並在出現問題時回滾到穩定版本。

我希望我能夠展示我們如何透過不斷增長的報價基礎使市場快速穩定。 我們如何解決伺服器問題、處理大量請求、提高服務的靈活性並在不中斷工作流程的情況下做到這一點。

來源: www.habr.com

添加評論