雲栖号資訊:【 點選檢視更多行業資訊】
在這裡您可以找到不同行業的第一手的上雲資訊,還在等什麼,快來!
在 Botify,我們工程團隊的核心價值觀之一是 ownership。我們賦予工程和産品團隊自主權以及靈活性,讓它們擁有自己的項目并完成這些項目。然而,随着我們在更大的技術棧上工作,團隊規模也越來越大,我們開始遇到一些關于如何共享工作成果的問題。
2016 年年底,我們想賦予工程師和産品經理更多的 local ownership,進而能快速輕松地将他們的産品和技術棧投入使用。為此,我們決定将 Django 應用程式拆分為微服務。
這個故事闡述了我們是怎樣失敗,以及如今我們為何又将這些服務遷回到單體應用。同時,我們還會花些時間來分享我們的經驗教訓。
在深入讨論前,我想強調下:本文的目的并非指責微服務。在 Botify,我們的理念是“使用最合适的工具”——我們認為,微服務并非此刻解決我們問題的适當工具。
開啟微服務之旅
首先,簡單介紹下 Botify 的曆史。Botify 于 2012 年在 Python/Django 技術棧上建立。到 2016 年初,整個 Botify 平台都是通過我們 Django 應用程式的負載均衡叢集提供服務。當時,我們有一個大約 15 人的産品和工程團隊。
為什麼選擇微服務?
以下兩個目标促使我們評估在技術棧中使用微服務的可能性:
1.提高開發速度:我們希望減少前端和後端之間的依賴關系,并增加 local ownership,進而将開發主體限定為一個小團隊;
2.統一技術:我們在巴黎招聘 Python/Django 工程師越來越多困難,是以我們就想,前後端都統一使用 JavaScript 會讓招聘工作更容易,因為我們在轉向“全棧”JavaScript 這樣一個單一的角色。
夢想要大,但要從小事做起
我們決定從身份驗證和授權棧入手實作第一個微服務。我們認為,應該從小處着手,将 Django 應用程式目前的一部分功能轉移到微服務中。我們建立了一個小型的 JavaScript 團隊,負責實作 NodeJS 後端,用于處理使用者及其帳戶和權限。我們稱之為 Customer Success。

痛點
組織
當你像我們一樣,在人為因素的驅動下做出技術決策時,你很快就會遇到問題。新團隊的組織和流程很難與現有的團隊并行不悖。由于特性是獨立開發的,很快就出現了知識分化。微服務内部的東西沒法共享,而跨棧代碼審查的缺失讓我們覺得失去了 ownership。
隔離
你可能已經注意到,在上面的架構中,微服務 Customer Success 并非完全隔離。從單體到微服務存在後端到後端的通信,反之亦然。這并不完全是壞事(似乎還很常見),但這還是會降低性能,并在兩個服務之間建立依賴關系。從長遠來看,這還意味着更複雜的機制,如 circuit breakers 和優雅的服務降級(為保證正常運作時間和可用性)。
我們還看到,在該模式下,我們主要的關系資料庫是共享的。在資料庫内部,一些表被映射到 Django 和 Express/Sequelize 的模型。換句話說,修改共享表的模式需要對微服務和單體進行同步修改。這是由糟糕的領域隔離所導緻的。
工具
随着時間的推移,我們學會了如何健壯地建構和部署我們的 Django 應用程式,但是,對于每個新的技術棧,我們必須重新掌握這個過程。雖然在微服務環境中,獨立部署是一個核心優勢,但與部署微服務相比,我們還是對部署單體更有信心。不過,我們花了比較少的時間就為我們的微服務實作了健壯、流暢的自動化部署。
在日常工作中,我們遇到的問題越來越多,為了讓架構更有效,我們需要不斷地修改解決方案。期間,我們針對 Django 技術棧做了一些工作,改進代碼、覆寫率、可測試性、依賴關系和性能。
我們颠覆了最初驅使我們采用微服務的模式
微服務技術棧是從目前流行的架構和開發團隊最熟悉的架構中選取的。然而,在 Botify,我們堅信,要為合适的工作選擇合适的工具:合适的工具不一定是最著名的工具,也不一定是我們已經知道的工具。
我們确定,是時候認真地(重新)考慮我們的微服務架構和 Customer Success 後端了。
去而複返,重回單體
鑒于每天都要在 JavaScript 身份驗證後端和 Django 子產品之間頻繁地來回切換,我們把工程師們召集在一起,權衡該架構的優缺點以及潛在的遷移成本。
我們做出大膽選擇,将身份驗證後端重新加入到 Django 單體中,并重新生成了我們在 2016 年從 Django 轉換到 Node 的 API 端點。為什麼要重新采用以前的做法?下面是其中一些原因。
為恰當的問題選擇恰當的方案
重要的是要記住我們的用例以及 Botify 為誰服務。我們的平台是一個企業級 B2B 服務,幫助 Web 上最大的網站改善他們的 SEO。我們的主要挑戰在于對客戶資料的分析,而不是我們的流量:我們每個月要處理 PB 級的資料,在一些長期運作的任務上,比如爬取或處理資料。
不過,我們不需要巨大的面向使用者的流量,因為在撰寫本文時,我們的使用者主要是 Web 上大型站點的 SEO 經理。處理使用者相關資料的微服務架構旨在服務于高流量的 B2C 平台,而 Botify 的挑戰在于動态地聚合數以 GB 的 SEO 資料,讓其在幾秒鐘内可用。對大約一萬名客戶的中繼資料以毫秒為機關進行響應,這項任務無需高度可伸縮的微服務架構。恰恰相反,我們的後端到後端通信減慢了這些簡單的檢索過程,花費了更多時間。
共享資源意味着同步部署
舉個例子,最近的一個項目是删除一個無人維護的依賴項,該依賴項會将 JSON 資料庫列作為文本處理,以便利用 PostgreSQL 中内置的列類型 jsonb。
然而,這些文本列是在兩個代碼庫之間共享的,逐漸遷移非常麻煩。同步部署多個後端很容易出錯,尤其是在規模大并存在負載均衡的情況下,這與微服務最初的好處背道而馳。在 Botify,我們總是喜歡用這個警示性的故事來說明我們不喜歡同步部署。這是個有趣的故事,如果你有時間的話,可以一讀。
分離的代碼庫淡化了知識、ownership 和聯系
微服務最初由少數人實作,是為了保護新來者不受無人維護的代碼庫的影響,同樣的人處理了所有的演進。這種現象違背了我們的代碼評審理念:任何人都能夠評審并了解正在發生的事情。在 Botify,我們喜歡開放式的工作方法,這樣,團隊中的任何人都可以檢視所有代碼并發表評論或提出建議。使用單獨的技術棧、團隊和語言,這讓我們失去了團隊合作和嚴格評審給開發團隊帶來的許多好處。
以上這些促使我們停用身份驗證微服務并将其端點遷回 Django 單體的主要原因。
停用微服務
停用微服務非常簡單,有許多方法可供選擇。
我們希望在終止 Customer Success 後端時,所作的更改盡可能少,這意味着要模拟微服務的行為,在單體中逐個重寫 API 端點并切換路由。這樣,更改隻會影響 API,而不影響前端,進而避免可怕的同步部署或 API 版本控制。
以下是我們停用微服務所采取的步驟:
識别路由:列出微服務提供的所有 API 路由,确定它們的用途和目标。先處理簡單的情況:有些路由可能沒用,或者沒有必要。
在目标後端建立路由:最麻煩的工作是在目标代碼庫中編寫相同的邏輯。相同的輸入,相同的輸出,還要最小化切換風險。
Y 分支(Y-branch)調用,比較結果:設定一個簡單的分支系統,使用兩個後端并比較響應,以發現不一緻的地方。
根據 QA 回報,進行修複:QA 大量而廣泛地審查邏輯,以發現任何遺留的 Bug。
逐漸擴大 Y 分支範圍,以使用新遷移的邏輯。
待所有邏輯都遷移到單體後,删除微服務,并完成清理工作。移除正在運作的執行個體和計算機。
我們可以很自豪地說,我們在 2 月 4 日 10:34 成功地停用了 NodeJS 身份驗證後端 Customer Success,沒有任何同步部署或 API 版本控制。
如前所述,停用後端非常簡單,使用這套方法,我們沒有遇到大的麻煩。
實際上,我們遇到的最令人沮喪的問題是我們自己找出的:在建構 Customer Success 後端時,我們選擇的政策是,不管 REST 調用的結果是什麼,總是傳回 200 響應,并使用布爾型的 success 和響應關鍵字 statusCode 提供更具體的錯誤資訊:
{
"errors": [
{
"message": "Organization was not found",
"code": 404,
"name": "NotFoundError"
},
],
"success": false,
"statusCode": 404
}
雖然這是一種常見的 REST 實踐,但它不适用于 Django REST 架構。在不更改契約的情況下,将這些端點從 Express 遷移到 Django,需要大量重寫 Django REST 架構的行為,才能比對我們的 Express 實作。我們沒有利用 HTTP 狀态碼來處理錯誤,而是對其進行了修改,這樣,我們就可以用相同的響應關鍵字傳回 200 狀态碼。令人失望,但也不是太糟糕。這種差異讓人心裡發癢,如果我們認為有必要,就會把它作為技術債務的一部分加以修正。好的一面是,我們的微服務定義了一種智能的跨源資源共享政策,這個必須要複制到我們的單體系統中。雖然一開始做正确的事情很麻煩,但它使我們能更好地保護應用程式免受惡意攻擊。
小結
本文不是要批評微服務,也不是要對 Django 和 Express REST 後端評短論長。每一種技術都自有其用途,但我們相信,要在正确的時間選擇正确的解決方案來建構我們的産品。如今,我們不必來回切換,也不用維護兩個後端,顯然,我們已經從中獲益。
順便提一下,當我們建構微服務後端時,我們實際上還同時建構了另一個後端,這個後端直到今天都還用着。它的功能與使用者以及我們的單體應用聯系比較少,是以它運作地很好,依賴和痛點也都更少。如果我們必須再次實作一個類似的特性,我們可能會在單體應用中實作,但是現在,既然它還沒出問題,我們就先不修複了。
對于某些用例和情況,我們仍然相信微服務。我們并不排除未來建立新的微服務的可能性。不管怎樣,我們已經從錯誤中吸取了教訓。
我們的團隊在組織結構上也有了變化: 現在實行小組制,有單獨的小組負責産品特性以及前端和後端工程師面臨的技術挑戰,還有一名産品經理和一名 QA 工程師。我們吸取教訓,每個小組都幾乎是獨立地設計和實作其特性,但代碼庫在小組間是共享的,代碼審查則是由 Botify 的所有工程師負責。這讓我們可以共享 ownership、知識和技術棧維護,并達到我們作為一個團隊希望達到的卓越技術水準。
【雲栖号線上課堂】每天都有産品技術專家分享!
課程位址:
https://yqh.aliyun.com/zhibo立即加入社群,與專家面對面,及時了解課程最新動态!
【雲栖号線上課堂 社群】
https://c.tb.cn/F3.Z8gvnK
原文釋出時間:2020-05-29
本文作者: David Wobrock
本文來自:“
InfoQ”,了解相關資訊可以關注“
”