Dodo IS 架構的歷史:後台路徑

Habr 正在改變世界。 我們寫博客已經一年多了。 大約六個月前,我們收到了 Khabrovites 的完全合乎邏輯的反饋:“Dodo,你到處說你有自己的系統。 這個系統是什麼? 為什麼披薩連鎖店需要它?

我們坐下來,思考並意識到你是對的。 我們試圖解釋我們手指上的一切,但它是支離破碎的,沒有一個完整的系統描述。 就這樣開始了漫長的收集信息、尋找作者和撰寫一系列關於 Dodo IS 的文章的旅程。 我們走吧!

致謝:感謝您與我們分享您的反饋。 多虧了他,我們終於描述了系統,編制了一份技術雷達,並將很快推出對我們流程的大量描述。 沒有你,我們會再坐在那裡 5 年。

Dodo IS 架構的歷史:後台路徑

系列文章“什麼是 Dodo IS?” 講述:

  1. Dodo IS 中的早期單體應用(2011-2015)。 (進行中...)
  2. 後台路徑:分離基地和總線。 (你在這裡)
  3. 客戶端路徑:基礎立面(2016-2017)。 (進行中...)
  4. 真正的微服務的歷史。 (2018-2019)。 (進行中...)
  5. 完成整體結構的鋸切和結構的穩定。 (進行中...)

如果您有興趣了解其他信息 - 請寫在評論中。

作者對時間順序描述的看法
我定期為新員工召開會議,主題是“系統架構”。 我們稱之為“Dodo IS 架構介紹”,它是新開發人員入職流程的一部分。 以一種或另一種形式講述我們的建築,講述它的特點,我已經產生了一種特定的歷史描述方法。

傳統上,我們將系統視為一組組件(技術或更高級別)、相互交互以實現某些目標的業務模塊。 如果這樣的觀點適合設計,那麼它不太適合描述和理解。 這裡有幾個原因:

  • 現實與紙上談兵不同。 並非一切都按預期進行。 我們對它的實際結果和工作方式很感興趣。
  • 一致的信息呈現。 事實上,您可以按時間順序從開始到當前狀態。
  • 從簡單到復雜。 不是普遍的,但在我們的情況下是這樣。 該架構從更簡單的方法轉向更複雜的方法。 通常通過複雜化,解決了實施速度和穩定性問題,以及非功能性需求列表中的許多其他屬性(就在這裡 很好地講述了與其他要求的對比複雜性)。

2011 年,Dodo IS 的架構是這樣的:

Dodo IS 架構的歷史:後台路徑

到了2020年,就變得有點複雜了,變成了這個樣子:

Dodo IS 架構的歷史:後台路徑

這種演變是如何發生的? 為什麼需要係統的不同部分? 做出了哪些架構決策,為什麼? 讓我們在本系列文章中一探究竟。

2016 年的第一個問題:服務為什麼要離開單體應用

該週期的第一篇文章將介紹最先從整體中分離出來的服務。 為了讓您了解情況,我將告訴您我們在 2016 年初的系統中遇到了哪些問題,我們必須處理服務分離問題。

一個單一的 MySql 數據庫,當時存在於 Dodo IS 中的所有應用程序都將它們的記錄寫入其中。 結果是:

  • 重負載(85% 的請求是閱讀)。
  • 基地長大了。 正因為如此,它的成本和支持就成了一個問題。
  • 單點故障。 如果一個寫入數據庫的應用程序突然開始更積極地執行此操作,那麼其他應用程序也會感受到這一點。
  • 存儲和查詢效率低下。 通常,數據以某種結構存儲,這種結構對某些場景很方便,但對其他場景不適合。 索引可以加快某些操作的速度,但會減慢其他操作的速度。
  • 有些問題是通過倉促的做緩存和讀副本到基地來解決的(這將另文報導),但只是給他們爭取時間,並沒有從根本上解決問題。

問題是單體本身的存在. 結果是:

  • 單一和罕見的版本。
  • 多人聯合開發難度大。
  • 無法引入新技術、新框架和庫。

基礎和單體的問題已被多次描述,例如,在 2018 年初崩潰的背景下(像蒙克一樣,或者說幾句技術債, Dodo IS 停止的那一天。 異步腳本 и 鳳凰家族渡渡鳥的故事。 渡渡鳥的大墜落 IS),所以我不會詳述。 我只想說,我們想在開發服務時提供更多的靈活性。 首先,這涉及到整個系統中負載最重和根目錄的那些 - Auth 和 Tracker。

後台路徑:分離基地和總線

章節導航

  1. 單體計劃 2016
  2. 開始卸載單體:Auth 和 Tracker 分離
  3. 授權是做什麼的?
  4. 負載從哪裡來?
  5. 卸載授權
  6. 追踪器做什麼?
  7. 負載從哪裡來?
  8. 卸貨追踪器

單體計劃 2016

以下是 Dodo IS 2016 單體的主要模塊,下方是其主要任務的記錄。
Dodo IS 架構的歷史:後台路徑
收銀員送貨。 為快遞員做賬,給快遞員下單。
聯絡中心. 通過運營商接受訂單。
現場. 我們的網站(dodopizza.ru、dodopizza.co.uk、dodopizza.by 等)。
驗證. 後台的授權和認證服務。
跟踪器. 廚房中的訂單跟踪器。 在準備訂單時標記準備狀態的服務。
餐廳收銀台. 在餐廳接單,收銀界面。
出口. 在 1C 中上傳報告以進行會計處理。
通知和發票. 廚房中的語音命令(例如,“新披薩到了”)+ 為快遞員打印發票。
值班經理. 輪班經理的工作界面:訂單列表、績效圖表、員工轉移到班次。
辦公室主管. 加盟商和經理的工作接口:員工接待、比薩店工作報告。
餐廳記分牌. 比薩店電視上的菜單顯示。
行政. 特定比薩店的設置:菜單、價格、會計、促銷代碼、促銷活動、網站橫幅等。
員工個人賬戶. 員工的工作時間表,員工信息。
廚房激勵板. 一個獨立的屏幕掛在廚房裡,顯示披薩製造商的速度。
傳播學. 發送短信和電子郵件。
文件儲存. 用於接收和發布靜態文件的自有服務。

解決問題的最初嘗試幫助了我們,但它們只是暫時的喘息。 它們並沒有成為系統解決方案,因此很明顯必須對鹼基做些什麼。 例如,將通用數據庫劃分為幾個更專業的數據庫。

開始卸載單體:Auth 和 Tracker 分離

然後從數據庫中記錄和讀取的主要服務比其他服務更多:

  1. 授權。 後台的授權和認證服務。
  2. 跟踪器。 廚房中的訂單跟踪器。 在準備訂單時標記準備狀態的服務。

授權是做什麼的?

Auth是用戶登錄後台的服務(客戶端有單獨的獨立入口)。 請求中還要求它確保存在所需的訪問權限,並且這些權限自上次登錄以來沒有更改。 通過它,設備進入比薩店。

例如,我們想在大廳懸掛的電視上打開一個顯示訂單完成狀態的屏幕。 然後我們打開auth.dodopizza.ru,選擇“以設備身份登錄”,出現一個代碼,可以在班長電腦上的一個特殊頁面輸入,表示設備(設備)的類型。 電視本身將切換到其比薩店所需的界面,並開始顯示那裡已準備好訂單的顧客姓名。

Dodo IS 架構的歷史:後台路徑

負載從哪裡來?

後台的每個登錄用戶都會訪問數據庫,針對每個請求訪問用戶表,通過 sql 查詢提取用戶,並檢查他是否具有對該頁面的必要訪問權限和權限。

每個設備只對設備表執行相同的操作,檢查其角色和訪問權限。 對master數據庫的大量請求導致其加載,這些操作造成了公共數據庫資源的浪費。

卸載授權

Auth 有一個隔離的域,即有關用戶、登錄名或設備的數據進入服務(暫時)並保留在那裡。 如果有人需要他們,那麼他會去這個服務獲取數據。

曾是。 原來的工作計劃如下:

Dodo IS 架構的歷史:後台路徑

我想解釋一下它是如何工作的:

  1. 來自外部的請求來到後端(有Asp.Net MVC),帶有一個會話cookie,用於從Redis(1)獲取會話數據。 它要么包含有關訪問的信息,然後打開對控制器的訪問 (3,4),要么不打開。
  2. 如果沒有訪問權限,則需要完成授權程序。 在這裡,為了簡單起見,它顯示為同一屬性中路徑的一部分,儘管它是到登錄頁面的過渡。 在積極的情況下,我們將獲得一個正確完成的會話並轉到後台控制器。
  3. 如果有數據,則需要檢查它在用戶數據庫中的相關性。 他的角色改變了嗎,現在不應該允許他出現在頁面上嗎? 在這種情況下,在收到會話(1)後,您需要直接進入數據庫並使用身份驗證邏輯層(2)檢查用戶的訪問權限。 接下來,要么轉到登錄頁面,要么轉到控制器。 如此簡單的系統,但並不十分標準。
  4. 如果所有過程都通過了,那麼我們將進一步跳過控制器和方法中的邏輯。

用戶數據與所有其他數據分離,存儲在單獨的成員表中,來自 AuthService 邏輯層的功能很可能成為 api 方法。 域邊界定義得非常清楚:用戶、他們的角色、訪問數據、授予和撤銷訪問權限。 一切看起來都可以在單獨的服務中取出。

變得。 所以他們這樣做了:

Dodo IS 架構的歷史:後台路徑

這種方法有很多問題。 例如,在進程內部調用方法與通過 http 調用外部服務是不一樣的。 操作的延遲、可靠性、可維護性、透明性完全不同。 Andrey Morevskiy 在他的報告中更詳細地談到了這些問題。 “微服務的 50 種陰影”.

身份驗證服務及其設備服務用於後台,即用於生產中使用的服務和接口。 客戶端服務(如網站或移動應用程序)的身份驗證單獨進行,無需使用 Auth。 分離花了大約一年的時間,現在我們再次處理這個話題,將系統轉移到新的身份驗證服務(使用標準協議)。

為什麼分手這麼久?
一路上有很多問題讓我們放慢了速度:

  1. 我們希望將用戶、設備和身份驗證數據從國家/地區特定的數據庫中整合到一個數據庫中。 為此,我們必須將所有表和用法從 int 標識符轉換為全局 UUId 標識符(最近修改了此代碼 Roman Bukin 《Uuid——小結構的大故事》 和開源項目 原語). 存儲用戶數據(因為它是個人信息)有其局限性,對於某些國家/地區,有必要單獨存儲它們。 但是用戶的全局id必須是。
  2. 數據庫中的許多表都有關於執行操作的用戶的審計信息。 這需要一種額外的一致性機制。
  3. 在創建 api 服務之後,有一段漫長而漸進的過渡到另一個系統的時期。 切換必須對用戶來說是無縫的,並且需要手動操作。

披薩店的設備註冊方案:

Dodo IS 架構的歷史:後台路徑

提取 Auth 和 Devices 服務後的總體架構:

Dodo IS 架構的歷史:後台路徑

注意. 2020 年,我們正在開發基於 OAuth 2.0 授權標準的新版 Auth。 該標準相當複雜,但對於開發直通身份驗證服務很有用。 在文章“授權的微妙之處:OAuth 2.0 技術概述» 我們 Alexey Chernyaev 試圖盡可能簡單明了地講述該標準,以便您節省學習時間。

追踪器做什麼?

現在介紹第二個加載的服務。 跟踪器執行雙重角色:

  • 一方面,它的任務是向廚房的員工展示當前正在處理的訂單,現在需要烹調的產品。
  • 另一方面,將廚房中的所有流程數字化。

Dodo IS 架構的歷史:後台路徑

當訂單中出現新產品(例如比薩餅)時,它會轉到 Rolling out 跟踪站。 在這個站,有一個比薩製造商拿了一個所需大小的麵包並將其擀開,之後他在跟踪器平板電腦上記錄他已經完成了他的任務並將擀好的面坯轉移到下一站 - “啟動” .

在那裡,下一個比薩餅師傅將比薩餅填滿,然後在平板電腦上註明他已完成任務並將比薩餅放入烤箱(這也是一個必須在平板電腦上註明的獨立站)。 這樣的系統從Dodo一開始就有,從Dodo IS存在的一開始就有。 它允許您完全跟踪和數字化所有交易。 此外,跟踪器建議如何烹飪特定產品,根據其製造方案指導每種類型的產品,存儲產品的最佳烹飪時間,並跟踪產品上的所有操作。

Dodo IS 架構的歷史:後台路徑這是平板電腦屏幕在跟踪器“Raskatka”站的樣子

負載從哪裡來?

每家比薩店都有大約五台帶追踪器的平板電腦。 2016 年,我們擁有 100 多家比薩店(現在超過 600 家)。 每個平板電腦每10秒向後端發出一次請求,並從訂單表(與客戶端和地址的連接)、訂單構成(與產品的連接和數量指示)、動機核算表(在其中跟踪按下的時間)。 當披薩製造商點擊跟踪器上的產品時,所有這些表中的條目都會更新。 訂單表是通用的,它還包含接受訂單時的插入、系統其他部分的更新和大量讀數,例如,掛在比薩店的電視上向客戶顯示完成的訂單。

在與負載鬥爭的時期,當一切都被緩存並轉移到 base 的異步副本時,這些與 tracker 的操作繼續到 master base。 不應該有任何滯後,數據應該是最新的,不同步是不可接受的。

此外,由於它們缺少自己的表和索引,因此無法編寫為它們的使用量身定制的更具體的查詢。 例如,跟踪器在訂單表上有一個比薩店的索引可能是有效的。 我們總是從跟踪器數據庫中刪除比薩店訂單。 同時,對於接到一個訂單,它落在哪家披薩店並不重要,更重要的是哪個客戶下了這個訂單。 並且意味著客戶端上的索引是必要的。 跟踪器也沒有必要在訂單表中存儲與訂單關聯的打印收據或獎金促銷的 ID。 我們的跟踪器服務不關心此信息。 在一個常見的單體數據庫中,表只能是所有用戶之間的折衷。 這是最初的問題之一。

曾是。 原來的架構是:

Dodo IS 架構的歷史:後台路徑

即使在被分成不同的進程之後,大部分代碼庫對於不同的服務仍然是通用的。 控制器下的所有東西都是單一的,並且存在於同一個存儲庫中。 我們使用了通用的服務方法、存儲庫、一個通用的基礎,其中放置了通用的表。

卸貨追踪器

跟踪器的主要問題是數據必須在不同數據庫之間同步。 這也是它與分離的Auth服務的主要區別,順序和它的狀態是可以改變的,應該在不同的服務中顯示。

我們在餐廳結賬處接受訂單(這是一項服務),它以“已接受”狀態存儲在數據庫中。 之後,他應該到達追踪器,在那裡他會多次改變自己的狀態:從“廚房”到“打包”。 同時,訂單可能會受到 Cashier 或 Shift Manager 界面的一些外部影響。 我將在表格中給出訂單狀態及其描述:

Dodo IS 架構的歷史:後台路徑
更改訂單狀態的方案如下所示:

Dodo IS 架構的歷史:後台路徑

不同系統之間的狀態會發生變化。 這裡的跟踪器不是數據關閉的最終系統。 在這種情況下,我們已經看到了幾種可能的分區方法:

  1. 我們將所有訂單操作集中在一項服務中。 在我們的例子中,此選項需要太多服務才能處理訂單。 如果我們停下來,我們將得到第二個整體。 我們不會解決問題。
  2. 一個系統調用另一個系統。 第二個選項已經更有趣了。 但是有了它,調用鍊是可能的(連鎖故障),組件的連通性越高,管理難度就越大。
  3. 我們組織事件,每個服務通過這些事件與另一個服務進行通信。 結果,選擇了第三個選項,根據該選項,所有服務開始相互交換事件。

我們選擇第三個選項的事實意味著跟踪器將擁有自己的數據庫,並且對於訂單中的每次更改,它都會發送一個關於此的事件,其他服務訂閱該事件並且該事件也屬於主數據庫。 為此,我們需要一些服務來確保服務之間的消息傳遞。

到那時,我們已經在堆棧中有了 RabbitMQ,因此最終決定將其用作消息代理。 該圖顯示了訂單從餐廳收銀台到跟踪器的轉換,它在跟踪器中更改了它的狀態並顯示在經理的訂單界面上。 變得:

Dodo IS 架構的歷史:後台路徑

訂單路徑逐步
訂單路徑從訂單源服務之一開始。 這是餐廳的收銀員:

  1. 在結賬時,訂單已完全準備好,是時候將其發送到跟踪器了。 跟踪器訂閱的事件被拋出。
  2. 跟踪器為自己接受訂單,將其保存到自己的數據庫中,生成事件“跟踪器已接受訂單”並將其發送到 RMQ。
  3. 每個訂單已經有多個處理程序訂閱了事件總線。 對我們來說,與整體基礎同步的那個很重要。
  4. 處理程序接收到一個事件,從中選擇對它重要的數據:在我們的例子中,這是訂單的狀態“已被跟踪器接受”,並在主數據庫中更新其訂單實體。

如果有人需要單片表 orders 中的訂單,那麼您也可以從那裡讀取它。 例如,班次管理器中的訂單界面需要這樣:

Dodo IS 架構的歷史:後台路徑

所有其他服務也可以從跟踪器訂閱訂單事件以供自己使用。

如果一段時間後訂單開始工作,那麼它的狀態首先在其數據庫(Tracker 數據庫)中發生變化,然後立即生成“OrderIn Progress”事件。 它還進入 RMQ,從那裡它在一個單一的數據庫中同步並交付給其他服務。 一路上可能會遇到各種各樣的問題,更多細節可以在Zhenya Peshkov的報告中找到 關於Tracker中Eventual Consistency的實現細節.

Auth 和 Tracker 更改後的最終架構

Dodo IS 架構的歷史:後台路徑

總結中間結果: 最初,我的想法是將 Dodo IS 系統九年的歷史打包成一篇文章。 我想快速簡單地談談進化的階段。 然而,坐下來看材料,我意識到一切都比看起來要復雜和有趣得多。

反思這些材料的好處(或缺乏),我得出結論,如果沒有完整的事件記錄、詳細的回顧和對我過去的決定的分析,持續發展是不可能的。

我希望了解我們的道路對您有用且有趣。 現在我面臨著選擇在下一篇文章中描述 Dodo IS 系統的哪一部分:寫評論或投票。

只有註冊用戶才能參與調查。 登入, 請。

您希望在下一篇文章中了解 Dodo IS 的哪一部分?

  • 企業排放佔全球 24,1%Dodo IS 中的早期單體(2011-2015)14

  • 企業排放佔全球 24,1%首批問題及其解決方案(2015-2016)14

  • 企業排放佔全球 20,7%客戶端路徑:外觀高於基礎(2016-2017)12

  • 企業排放佔全球 36,2%真正的微服務的歷史(2018-2019)21

  • 企業排放佔全球 44,8%完整鋸切整體結構並穩定架構26

  • 企業排放佔全球 29,3%關於系統開發的進一步計劃17

  • 企業排放佔全球 19,0%我不想了解 Dodo IS11 的任何信息

58 位用戶投票。 6 名用戶棄權。

來源: www.habr.com

添加評論