本文是關于 Newbe.Claptrap 項目主體内容的介紹,讀者可以通過這篇文章,大體了解項目内容。
輪子源于需求
随着網際網路應用的蓬勃發展,相關的技術理論和實作手段也在被不斷創造出來。諸如“雲原生架構”、“微服務架構”、“DevOps”等一系列關鍵詞越來越多的出現在工程師的視野之中。總結來看,這些新理論和新技術的出現,都是為了解決網際網路應用中出現的一些技術痛點:
更高的容量擴充性要求。在商業成功的基礎前提下,網際網路應用的使用者數量、系統壓力和硬體裝置數量等方面都會随着時間的推移出現明顯的增長。這就對應用本省的容量可擴充性提出了要求。這種容量可擴充性通常被描述為“應用需要支援水準擴充”。
更高的系統穩定性要求。應用程式能夠不間斷運作,確定商業活動的持續進展,這是任何與這個應用系統相關的人員都希望見到的。但是要做到這點,通常來說是十分困難的。而現今的網際網路應用在面對諸多同類競争者的情況下,如果在這方面做得不夠健全,那麼很可能會失去一部分使用者的青睐。
更高的功能擴充性要求。“擁抱變化”,當人們提到“靈活項目管理”相關的内容時,都會涉及到的一個詞語。這個詞語充分展現了當今的網際網路應用若要成功,在功能性上做到出彩做到成功是多麼的重要。也從一個側面展現了目前網際網路環境下産品需求的多變。而作為系統工程師,在應用建立之初就應該考慮這點。
更高的開發易用度要求。這裡所屬的開發易用度是指,在應用系統自身在進行開發時的難易程度。要做到越易于開發,在應用自身的代碼結構,可測試性,可部署性上都需要作出相應的努力。
更高的性能要求。這裡提到的性能要求,是特指在系統容量增加時的性能要求。避免系統的單點性能問題,讓應用系統具備可水準擴充的特性。通常來說,在性能出現問題時,若可以通過增加實體裝置來解決問題,通常來說是最為簡單的辦法。而在不同的系統容量之下,系統性能的優化方案通常是不同的。是以結合應用場景進行技術方案的選型一直都是系統工程師所需要考慮的問題。
本項目,就是基于以上這些系統功能特性要求所總結出來的一套開發架構。這其中包含了相關的理論基石、開發類庫和技術規約。
世界上本也不存在“銀彈”。一套架構解決不了所有問題。 ——不願意透露姓名的月落
從需求出發
在講解分布式系統時,常常會用到“賬号轉賬”這個簡單的業務場景來配合描述。這裡闡述一下這個業務場景。
假設我們需要建設一個具備賬号體系的業務系統。每個賬号都有餘額。現在需要執行一次轉賬操作,将賬号A的餘額中的300劃轉給賬号B。另外,基于上節的基本要求,我們在實作這個場景時,需要考慮以下這些内容:
- 需要應對系統容量的激增。應用初期可能隻有1000個初始使用者。由于應用推廣效果良好以及機器人賬号的湧入,使用者數量實作了在一個月内實作了三個數量級的攀升,也就是增長到了百萬級别。
- 需要考慮系統的穩定性和可恢複性。盡可能減少系統整體的平均故障時間,即使出現系統故障也應該是盡可能易于恢複的。也就是,要避免出現單點故障。
- 需要考慮業務的可擴充性。後續可能需要增加一些業務邏輯:按照賬戶等級限制日轉賬額、轉賬成功後進行短信通知、轉賬支援一定額度的免密轉賬、特定的賬号實作“T+1”到賬。
- 需要考慮代碼的可測試性。系統的業務代碼和系統代碼能夠良好的分離,能夠通過單元測試的手段初步驗證業務代碼和系統代碼的正确性和性能。
輪子的理論
本節将介紹一些和本架構緊密結合的理論内容,便于讀者在後續的過程中了解本架構的工作過程。
Actor模式
Actor模式是一種并發程式設計模型。通過這種程式設計模型的應用可以很好的解決一些系統的并發問題。這裡所提到的并發問題是指計算機對同一資料進行邏輯處理時,可能由于存在多個同時發起的請求可能導緻資料出現不正确的問題。這個問題在進行多線程程式設計時一定會遇到的問題。舉個簡單的例子,假如在不加同步鎖的情況下,使用100個線程并發對記憶體中的一個
int
變量執行
++
操作。那麼最終這個變量的結果往往小于100。此處Actor模式是如何避免此問題的。
首先,為了便于了解,讀者在此處可以将Actor認為是一個對象。在面向對象的語言(Java、C#等)當中,可以認為Actor就是通過
new
關鍵詞建立出來的對象。不過這個對象有一些特别的特性:
擁有屬于自身的狀态。對象都可以擁有自身的屬性,這是面向對象語言基本都具備的功能。在Actor模式中,這些屬性都被統稱為
Actor的狀态(State)
。Actor的狀态由Actor自身進行維護。
這就強調了兩點:
第一、Actor的狀态隻能由自身進行改變,若要從外部改變Actor的狀态,隻能通過調用Actor才能改變。

第二、Actor的狀态隻在Actor内部進行維護,不與目前Actor之外的任何對象共享。這裡說的不共享也是強調其不能通過外部某個屬性的改變而導緻Actor内部狀态的變化。這點主要是為了差別于一些具備“對象引用”語言特性的程式設計語言而言的。例如:在C#的
class
的
public
屬性,假如是引用類型,那麼在外部獲得這個
class
之後是可以改變
class
中的屬性的。但是這在Actor模式當中是不被允許的。
不過從Actor内部讀取資料到外部,這仍然是允許的。
單線程。Actor通常同一時間隻能接受一個調用。這裡所述的線程不完全是指計算機中的線程,是為了凸顯“Actor同一時間隻能處理一個請求的特性”而使用的詞語。假如目前Actor正在接受一個調用,那麼剩餘的調用都會阻塞,直到調用結束,下一個請求才允許被進入。這其實類似于一個同步鎖的機制。通過這種機制就避免了對Actor内部狀态進行修改時,存在并發問題的可能。具體一點說明:如果使用100個線程對一個Actor進行并發調用,讓Actor對狀态中的一個
int
變量進行
++
操作。最終這個狀态的數值一定是100。
不過單線程也不是絕對的,在不存在并發問題的請求情況下,允許并發處理。例如讀取Actor中的狀态,這通常不會有并發問題,那麼此時就允許進行并發操作。
讀到Actor單線程特性時,通常讀者會考慮到這是否會導緻Actor本身處理過慢而産生性能問題呢?關于這點,希望讀者繼續持有這個問題往後閱讀,尋找答案。
事件溯源模式
事件溯源模式是一種軟體設計思路。這種設計思路通常與傳統的采用增删查改(CRUD)為主的系統設計思路相差別。CRUD應用通常存在一些局限性:
- 通常來說CRUD應用會采用直接操作資料存儲的做法。這樣的實作方式可能會由于對資料庫優化不足而導緻性能瓶頸,并且這種做法會較難實作應用伸縮。
- 在特定的領域通常存在一些資料需要注意對并發問題進行處理,以防止資料更新的錯誤。這通常需要引入“鎖”、“事務”等相關的技術來避免此類問題。但這樣又有可能引發性能上的損失。
- 除非增加額外的審計手段,否則通常來說資料的變更曆史是不可追蹤的。因為資料存儲中通常儲存的是資料最終的狀态。
與CRUD做法對比,事件溯源則從設計上避免了上述描述的局限性。接下來圍繞上文中提到的“轉賬”業務場景簡述事件溯源的基礎工作方式。
采用CRUD的方法實作“轉賬”。
采用事件溯源的方式實作“轉賬”。
如上圖所示,通過事件溯源模式将轉賬業務涉及的餘額變動采用事件的方式進行存儲。同樣也實作了業務本身,而這樣卻帶來了一些好處:
- 通過事件,可以還原出賬号任何階段的餘額,這就一定程度實作了對賬号餘額的跟蹤。
- 由于兩個賬号的事件是獨立處理的。是以,兩個賬号的處理速度不會互相影響。例如,賬号B的轉入可能由于需要額外的處理,稍有延遲,但賬号A仍然可以的轉出。
- 可以通過訂閱事件來做一些業務的異步處理。例如:更新資料庫中的統計資料,發送短信通知等其他的一些異步操作。
當然引入事件溯源模式之後也就引入了事件溯源相關的一些技術問題。例如:事件所消耗的存儲可能較為巨大;不得不應用最終一緻性;事件具備不可變性,重構時可能較為困難等。相關的這些問題在一些文章中會有較為細緻的說明。讀者可以閱讀後續的延伸閱讀内容,進而進行了解與評估。
業務複雜度是不會因為系統設計變化而減少的,它隻是從一個地方轉移到了另外的地方。——總說自己菜的月落
讓輪子轉起來
基于讀者已經大體了解了上節理論的基礎上,本節将結合上述描述的“轉賬”業務場景,介紹本架構的工作原理。首先讀者需要了解一下本架構的兩個名詞。
Claptrap
Claptrap是本架構定義的一種特殊Actor。除了上文中提到Actor兩種特性之外,Claptrap還被定義為具有以下特性:
狀态由事件進行控制。Actor的狀态在Actor内部進行維護。Claptrap同樣也是如此,不過改變Claptrap的狀态除了在Actor之外,還限定其隻能通過事件進行改變。這就将事件溯源模式與Actor模式進行了結合。通過事件溯源模式保證了Actor狀态的正确性和可追溯性。這些改變Claptrap狀态的事件是由Claptrap自身産生的。事件産生的原因可以是外部的調用也可以是Claptrap内部的類觸發器機制産生的。
Minion
Minion是本架構定義的一種特殊Actor。是在Claptrap基礎上做出的調整。其具備以下特性:
從對應的Claptrap讀取事件。與Claptrap相同,Minion的狀态也由事件進行控制。不同的是,Minion就像其字面意思一樣,總是從對應的Claptrap處擷取事件,進而改變自身的狀态。是以,其可以異步的處理Claptrap産生事件之後的後續操作。
業務實作
接下來有了前面的基礎介紹,現在介紹一下本架構如何實作上文中的“轉賬”場景。首先可以通過下圖來了解一下主要的流程:
如上圖所示,整個流程便是本架構實作業務場景的大體過程。另外,還有一些需要指出的是:
- 圖中Client與Claptrap的調用等待隻有第一階段的時候存在,也就是說,這使得Client可以更快的得到響應,不必等待整個流程結束。
- Claptrap A 在處理完自身請求,并将事件發送給 Minion A 之後就可以重新接受請求,這樣提高了Claptrap A的吞吐量。
- Minion不僅僅隻能處理Claptrap之間的調用代理。在Minion當中還可以根據業務需求進行:發送短信,更新資料庫統計資料等其他操作。
- Minion也可以具備自己的狀态,将部分資料維持在自身的狀态中以便外部可以從自身進行查詢,而不需要從對應的Claptrap中進行查詢。例如:統計該賬号最近24小時的轉賬變動,以便快速查詢。
業務容量
前文提到本架構需要建設的是一個可以水準擴充的系統架構,隻有如此才能應對業務容量的持續增長。在這點上,本架構現階段采用的是微軟開源的Orleans和Service Fabric搭配,實作應用程式和實體裝置的放縮。當然,涉及資料存儲部分時勢必也涉及到資料庫叢集等一系列問題。這些屬于技術應用的細節,而非架構理論設計的内容。是以,此處隻表明本架構可以基于以上的開源架構進行容量放縮。應用過程中的實際問題,讀者可以在後續的項目内容中尋求解答。
輪廠現狀
目前項目正處于設立初期,主體的體系結構設計與編碼仍在進行中,讀者可以通過項目位址了解項目的最新進展:
Github,主要項目源碼管理:https://github.com/newbe36524/Newbe.Claptrap
Gitee,碼雲倉庫位址:https://gitee.com/yks/Newbe.Claptrap
點選連結QQ交流【Newbe.Claptrap】:https://jq.qq.com/?_wv=1027&k=5uJGXf5
延伸閱讀
以下這些内容都對本架構産生了深遠的影響。讀者可以通過閱讀以下這些内容,增加對本架構的了解。
- 基于Actor架構Orleans建構的分布式、事件溯源、事件驅動、最終一緻性的高性能架構——Ray
- Event Sourcing Pattern
- Event Sourcing Pattern 中文譯文
- Orleans - Distributed Virtual Actor Model
- Service Fabric
- ENode 1.0 - Saga的思想與實作