剛做後端大概10個月,從游戲前端開發轉向後端,看似熟悉的編程語言,在不同的領域內實際上要考慮的事情也是全然不同的。 當我們談論後端開發,自然而然聯想到,後端是服務於前端的,也是承載、服務於業務的一個重要組成部分。系統的穩定性,正確性以及可用性都是需要考慮的問題。 做後端,說簡單也簡單,說難也很難,簡 ...
剛做後端大概10個月,從游戲前端開發轉向後端,看似熟悉的編程語言,在不同的領域內實際上要考慮的事情也是全然不同的。
當我們談論後端開發,自然而然聯想到,後端是服務於前端的,也是承載、服務於業務的一個重要組成部分。系統的穩定性,正確性以及可用性都是需要考慮的問題。
做後端,說簡單也簡單,說難也很難,簡單是因為你只需要對數據進行增刪查改,聚合統計就完事了,說難是因為一旦涉及到可用性,必然離不開分散式,然而我們知道,由分散式而引出的一系列問題才是最為棘手的,更不用說系統安全、部署、監控等一些列的落地措施。
你需要確保多個系統的業務數據不混亂,在某些跨服務功能的調用鏈中保持一致性,當某一個服務調用失敗之後進行重試或者回滾。
重試需要你的服務具備冪等性,也就是說,當引入重試機制之後,你需要確保一個服務在同一個業務上下文中多次調用而不會產生額外的影響,例如銀行轉賬,如果多扣幾次錢或者多加了幾次錢對於公司的業務以及信譽來說都是無法接受的,解決這個問題的辦法需要引入類似流水號的關聯標識,在不同的服務裡面形成一個標識上下文。
當我們的整個後端系統根據業務領域分割為不同的服務獨立成型時,多個服務之間相互調用往往會因為網路原因而失敗。硬體損壞、宕機、自然災害等一些列不可抗力因素告訴我們,任何事情都有可能失敗,當你的系統規模超過閾值之後,偶然將會轉變為必然。我們要的做的,不應該是想出一個萬全之策強力約束保證我們的系統一定會成功,或者懷著僥幸的心理上線然後惶惶不安的等待系統出現問題。反而更應該思考一下,當系統出現問題之後,如何快速有效的修複失敗所帶來的影響。因為我們知道,既然我們無法保證100%成功,那唯一的出路便想出一個補救措施來預防失敗,這往往會更令人更有安全感。
在討論如何解決分散式一致性之前,我們更應該反過頭來看一下,什麼是事務,它在單機模式下是如何保證業務的原子性的。我們往往將業務事務和資料庫的事務混為一談,通常來說這是沒問題的,因為後端做的不就是數據的存儲麽。但當我們把這兩個進行區分的時候,會發現大多數的時候,我們所說的事務,僅僅是通過資料庫提供的ACID來實現的。當你要對資料庫的多個表進行操作的時候,打開一個事務,並且進行相應的更新,然後提交事務,此時要麼成功,要麼回滾撤銷變更,通過資料庫提供的ACID保證我們的數據不會因為受到中斷的影響而產生混亂。
但如果我們分散式之後呢?每一個業務子系統擁有自己的資料庫,你如何保證一個業務流程,在跨子系統調用時保證原子性呢?
如果我們把希望繼續寄托於資料庫,多個業務子系統使用同一個資料庫,資料庫本身將會成為瓶頸,這樣便失去了一開始設計分散式系統的初衷。
如果某一個子系統失敗之後,造成了數據的不一致,此時,要麼回滾之前的操作,要麼就想辦法讓這個失敗的調用變為成功。
A作為業務發起者調用B和C,B成功,C失敗,A知道C失敗之後該怎麼做呢?是重新調用一次C還是通知B回滾呢?如果通知B回滾的時候也失敗了呢?
答案往往是不統一的,每一個業務領域都有根據自身情況進行處理的方式。有很多分散式事務協議能夠解決些許問題,但是這往往會造成複雜度的提升,導致將來的維護以及拓展變得舉步維艱。
CAP定理相信大家都有瞭解,在任何時候我們要麼取CP要麼取AP,CP的問題在於複雜、可用性以及可維護性差,AP的問題在於一定的時間內,數據會存在不一致性。如果選擇CP,是時候考慮一下業務的分割是否合理,只有極少的情況我們才需要保證強一致性。
我們需要一種更為聰明的方法,一種機制來保證我們的系統能夠達成最終一致性,這才是分散式系統需要解決問題的根本之道,並且已經有很多模式方法來幫助我們達成我們想要的結果。
換一個角度來講,分散式事務,本身就是一個偽命題,因為系統是脆弱的,有太多太多的因素會造成事情不可控。
我們要做對的事情,讓系統能夠根據業務需求變更逐步演進,形成一個良性迴圈,根據業務需求、目標領域來設計系統,合理的進行分割才是王道。
如果你的系統本身不匹配業務領域,而又把不合理、生搬硬套的架構應用於它,就會出現各種各樣的問題,這些問題在某些時候甚至是不可能解決的。
在我看來,有關於解決分散式事務的技術,無非是在為糟糕的設計進行彌補,導致系統到最後臃腫不堪。
一件事情,分散式系統在絕大多數時候,反脆弱、最終一致性才是我們真正需要去解決的問題。
PS:你的系統究竟有多大才會導致最終一致性會被人為的察覺出來?但我們知道數據最終將會一致不是嗎?:)