這裏所說的三架馬車是指微服務、消息隊列和定時任務。以下圖所示,這裏是一個三駕馬車共同驅動的一個立體的互聯網項目的架構。無論項目是大是小,這個架構模板的形態一旦定型了以後就不太會變,區別只是咱們有更多的服務有更復雜的調用,更復雜的消息流轉,更多的Job,整個架構總體是可擴展的,並且不會變形,這個架構能夠在很長的一段時間內無需有大的調整。數據庫
圖上畫了虛線框的都表明這個模塊或項目是不包含太多業務邏輯的,純粹是一層皮(會調用服務可是不會觸碰數據庫)。黑色線的箭頭表明依賴關係,綠色和紅色箭頭分別是MQ的發送和訂閱消息流的方向。具體在後文都會進一步詳細說明。api
微服務並非一個很新的概念,在10年前的時候我就開始實踐這個架構風格,在四個公司的項目中全面實現了微服務,愈來愈堅信這是很是適合互聯網項目的一個架構風格。不是說咱們的服務必定要跨物理機器進行遠程調用,而是咱們經過進行有意的設計讓咱們的業務在一開始的時候就按照領域進行分割,這能讓咱們對業務有更充分的理解,能讓咱們在以後的迭代中輕易在不一樣的業務模塊上進行耕耘,能讓咱們的項目開發愈來愈輕鬆,輕鬆來源於幾個方面:緩存
這裏也要求咱們作到幾個方面的原則:網絡
a. 三方合做服務PartnerInvestService:對接合做的三方理財平臺的流量數據結構
b. 普通投資服務NormalInvestService:最普通形態的資產的主流程架構
c. 預定投資產品服務ReserveInvestService:須要預定投資的資產的主流程併發
d. 週期性計劃產品服務AutoInvestService:會按期自動復投的理財產品主流程app
e. 投資人交易服務TradeService:專門負責處理投資人的交易行爲,好比投資負載均衡
f. 借款人交易服務LoanService:專門負責處理借款人的交易行爲,好比還款框架
g. 用戶服務UserService:處理用戶的註冊登陸等
h. 資產服務ProjectService:處理資產和標的相關
i. 帳戶帳務服務AccountService:處理用戶的帳戶各個子帳戶和帳務記錄
j. 營銷活動服務ActivityService:處理各類活動、用戶的積分體系
k. 會員體系服務VipService:處理用戶的會員成長體系
l. 銀行存管服務BankService:專門用於對接銀行存管系統
m. 電子簽章服務DigSignService:專門用於對接三方數字簽章系統
n. 消息推送服務MessageService:專門用於對接三方短信通道和推送SDK
a. 聚合業務服務:高層次的串起來整個流程的具備完整業務形態的業務服務。和基礎業務服務不一樣的是,這裏是在完整描述一方面的業務,這個業務每每是由各類基礎業務拼裝組合起來的。和不一樣外部合做方的不一樣合做形式,給用戶提供的產品的不一樣服務形態,都決定了聚合業務服務會有業務流程上的差別化,若是把此類服務下放到基礎業務服務中,那麼基礎業務服務會有各類if-else邏輯(根據產品類型、用戶類型進行各類if-else),隨着業務的合做不合做,需求變更,基礎業務服務會腐化得很厲害,爲了不這個狀況,咱們把變更的多的聚合業務邏輯放到獨立的業務服務中。通常而言,聚合業務服務由於表明了獨立的業務流程,它們之間是不會進行相互調用的,可是它們必定會調用大量的各種基礎業務服務。在第一點裏說的標有藍色字體的a~d這些服務都是此類服務。這個層次的服務的業務邏輯更可能是在表達業務流程的複雜性和差別性,不會涉及到具體怎麼處理帳戶信息、帳務信息、用戶信息,不會涉及到怎麼處理具體的投資人和借款人的交易。好比對於預定這類業務形態,它關注的是先要預定資產,而後再由系統進行自動投資,底層徹底依賴於投資人交易服務來作整個交易的過程。
b. 基礎業務服務:某一個領域業務相關的服務。此類服務之間是容許相互調用的,好比投資人交易服務和借款人交易服務免不了須要和用戶服務、資產服務、帳戶帳務服務進行通信作相關的用戶信息查詢、標的信息查詢、記帳等業務操做。之因此投資人交易服務和借款人交易服務定位爲基礎業務服務是由於,它們處理的是仍是某一個具體方面的業務,並非全流程,在這個抽象層次上,業務不是那麼容易變更的,對於複雜的各類業務形態(好比預定交易、自動復投交易、等額本息交易)會在這些服務之上造成聚合業務服務。在第一點裏說的標有綠色字體的e~k這些服務都是此類服務。在這個層次的服務雖然擁有大量的業務邏輯,可是其實已經享受到了很大層度的公共基礎服務的重用了,並且和本身業務耦合較弱的額外邏輯每每沒有在本服務中堆積,由更多專職的基礎業務服務來承擔了這部分邏輯。
c. 公共基礎服務:負責某一個方面的基礎業務(沒有什麼領域業務邏輯在裏面),能夠是自治的處理某一個方面的基礎業務,也能夠和外部通信實現某一個方面的功能,服務之間是不會相互調用的,可是會被聚合業務服務和基礎業務服務調用。在第一點裏說的標有橙色字體的l~n這些服務都是此類服務。若是之後和外部的合做有變更,由於咱們已經定義了對外的服務契約,能夠輕易替換這個服務來更換合做的第三方,系統其他的地方几乎不須要修改。全部的三方對接都建議獨立出公共基礎服務,若是同一個業務對接多個三方渠道,好比推送對接了極光和個推,甚至公共基礎服務還能夠由一個抽象聚合的推送服務,下面再路由到具體的極光推送和個推推送服務。
但願在這裏把這個事情說清楚了,怎麼來劃分服務怎麼劃分三個層次的服務是一個頗有意思頗有必要的事情,在服務劃分以後最好有一個明確的文檔來描述每個服務的職責,這樣咱們在無需閱讀API的狀況下能夠大概定位到業務所在的服務,整個複雜的系統就變得很直白了。
a. 能夠很方便地重構底層的數據結構甚至是數據源,只要接口不變,外部不會感知到。
b. 性能有問題的狀況下須要加緩存、分表、拆庫、歸檔是比較方便的事情,畢竟數據源沒有外部依賴。
說白了就是個人數據我作主,我想怎麼搞外面管不着,在重構或是作一些高層次技術架構(好比異地多活)的時候,沒有底層數據被依賴,這過重要了。固然,壞處或是麻煩的地方就是跨服務的調用使得數據操做沒法在一個數據庫事務中完成,這並非什麼大問題,一是由於咱們這種拆分方式並不會讓粒度太細,大部分的業務邏輯是在一個業務服務裏完成的,二是後面會提到跨服務的調用無論是經過MQ進行的仍是直接調用進行的,都會有補償來實現最終一致性。
a. 咱們在對外提供服務的時候,不但要告知用戶服務提供的業務能力,還要告知用戶服務的特性,好比是不是冪等的(對於訂單類型的操做服務,相同的訂單相同的操做強烈建議是冪等的,這樣調用方能夠放心進行重試或補償);是否須要外部進行補償(在這裏你可能說爲何須要外部進行補償,服務就不能本身補償嗎,對於內部的子邏輯服務固然能夠本身補償,可是有的時候由於網絡緣由請求就沒有到服務端,服務端一無所知這個調用固然無從去補償);是否有頻控的限制;是否有權限的限制;降級後的處理方式等等。
b. 反過來,咱們調用其它服務也須要多問幾句目標服務的特性,針對性進行設計相應的補償邏輯、一致性處理邏輯和降級邏輯。咱們必須考慮到有些時候並非服務端的問題,而是請求根本沒有到達服務端。
c. 服務自己每每也會有複雜的邏輯,做爲客戶端的身份調用大量外部的服務,因此服務端和客戶端的角色不是固定不變的,當咱們的服務內部有許多客戶端來調用服務端的時候,對於每個子邏輯咱們都須要仔細考慮每個環節。否正會出現的狀況就是,這個服務是部分邏輯冪等的或是部分邏輯是具有最終一致性的。
若是你說,這麼多服務,我在實現的時候很難考慮到這些點,我徹底不去考慮分佈式事務、冪等性、補償(絕不誇張地說,有的時候咱們花了20%的時間實現了業務邏輯,而後花80%的時間在實現這些可靠性方面的外圍邏輯),行不行?也不是不能夠,那麼業務在線上跑的時候必定會是千瘡百孔的,若是整個業務的處理對可靠性方面的要求不高或是業務不面向用戶不會受到投訴的話,這部分業務的是能夠暫時不考慮這些點,可是諸如訂單業務這種核心的不容許有不一致性的業務仍是須要全面考慮這些點的。
a. 經過使用分頁的形式,一次返回固定的少許數據,客戶端按需拉取更多數據。
b. 能夠在參數中傳相似於EnumSet的數據結構,讓客戶端告知服務端我須要什麼層次的數據,好比GetUserInfo接口能夠提供給客戶端BasicInfo、VIPInfo、InvestData、RechargeData、WithdrawData,客戶端能夠按需從服務端拿BasicInfo|VipInfo。
最後不得不說,在整個公司都搞起了微服務後,跨部門的一些服務調用在商定API的時候不免會有一些扯皮的現象發生,究竟是我傳給你呢仍是你本身來拉,這個數據對我沒用爲何要在我這裏留一下呢?拋開非技術層面的事情不說,這些扯皮也是有一些技術手段來化解的:
a. 明確服務職責,也就明確了服務應該感知到什麼不該該感知到什麼。 b. 跨部門的服務交互的接口定義能夠定的很輕,採用只有一個訂單號的接口或MQ通知+數據回拉的策略(誰數據多誰提供數據接口,不用把數據一次性推給下游)。 c. 數據提供方能夠構建一套通用數據接口,這樣能夠知足多個部門的需求,無需作定製化的處理。甚至在接口上能夠提供落地和不落地兩種性質的透傳。
你可能看到這裏以爲很頭暈,爲何微服務須要額外考慮這麼多東西,實現的複雜度一會兒上升了。我想說的是咱們須要換一個角度來考慮這個事情:
可是,若是服務粒度劃分的不合理,層次劃分的不合理,底層數據源有交叉,沒考慮到網絡調用失敗,沒考慮到數據量,接口定義不合理,版本升級過於魯莽,整個系統會出各類各樣的擴展問題性能問題和Bug,這是很頭痛的,這也就須要咱們有一個完善的服務框架來幫助咱們定位各類不合理,在以後說到中間件的文章中會再具體着重介紹服務治理這塊。
消息隊列MQ的使用有下面幾個好處,或者說咱們每每處於這些目的來考慮引入MQ:
這些需求互聯網項目中基本都存在,因此消息隊列的使用是很是重要的一個架構手段。在使用上有幾個注意點:
定時任務的需求有那麼幾類:
詳細說明一下任務驅動是怎麼一回事。其實在數據庫中作一些任務表,以這些表驅動做爲整個數據處理的核心體系,這套被動的運做方式是最最可靠的,比MQ驅動或服務驅動兩種形態可靠多,天生必然是可負載均衡的+冪等處理+補償到底的,任務表能夠設計下面的字段:
· 自增ID
· 任務類型:代表具體的任務類型,固然也能夠不一樣的任務類型直接作多個任務表。
· 外部訂單號:和外部業務邏輯的惟一單號關聯起來。
· 執行狀態:未處理(等待處理),處理中(防止被其它Job搶佔),成功(最終成功了),失敗(暫時失敗,會繼續進行重試),人工介入(永遠不會再變了,必定須要人工處理,須要報警通知)
· 重試次數:處理過太屢次仍是失敗的能夠歸類爲死信,由專門的死信隊列任務單獨再進行若干次的重試不行的話就報警人工干預
· 處理歷史:每一次的處理結果,Json的List保存在這裏供參考
· 上次處理時間:最近一次執行時間
· 上次處理結果:最近一次執行結果
· 建立時間:數據庫維護
· 最後修改時間:數據庫維護
除了這些字段以外,還可能會加一些業務本身的字段,好比訂單狀態,用戶ID等等信息做爲冗餘。任務表能夠進行歸檔減小數據量,任務表扮演了消息隊列的性質,咱們須要有監控能夠對數據積壓,出入隊不平衡處理不過來,死信數據發生等等狀況進行報警。若是咱們的流程處理是任務ABCD順序來處理的話,每個任務由於有本身的檢查間隔,這套體系可能會浪費一點時間,沒有經過MQ實時串聯這麼高效,可是咱們要考慮到的是,任務的處理每每是批量數據獲取+並行執行的,和MQ基於單條數據的處理是不同的,整體上來講吞吐上不會有太多的差別,差的只是單條數據的執行時間,考慮到任務表驅動執行的被動穩定性,對於有的業務來講,這不失爲一種選擇。
這裏再說明一下Job的幾個設計原則:
三馬車都說完了,那麼,最後咱們來梳理一下這麼一套架構下整個項目的模塊劃分:
· Site:
o console
o app-gateway
· Façade Service:
o partnerinvestservice-api
o partnerinvestservice-server
o partnerinvestservice-listener
o normalinvestservice-api
o normalinvestservice-server
o normalinvestservice-listener
o reserveinvestservice-api
o reserveinvestservice-server
o reserveinvestservice-listener
o autoinvestservice-api
o autoinvestservice-server
o autoinvestservice-listener
· Business Service:
o tradeservice-api
o tradeservice-server
o tradeservice-listener
o loanservice-api
o loanservice-server
o loanservice-listener
o userservice-api
o userservice-server
o projectservice-api
o projectservice-server
o accountservice-api
o accountservice-server
o accountservice-listener
o activityservice-api
o activityservice-server
o activityservice-listener
o vipservice-api
o vipservice-server
o vipservice-listener
· Foundation Service:
o bankservice-api
o bankservice-server
o digsignservice-api
o digsignservice-server
o messageservice-api
o messageservice-server
· Job:
o scheduler-job
o task-job
o compensation-job
這每個模塊均可以打包成獨立的包,全部的項目不必定都要在一個項目空間內,能夠拆分爲20個項目,服務的api+server+listener放在一個項目內,這樣其實有利於CICD缺點就是修改代碼的時候須要打開N個項目。
以前開篇的時候說過,使用這套簡單的架構既可以有很強的擴展餘地,複雜程度上或者說工做量上不會比All-In-One的架構多多少,看到這裏你可能以爲並不一樣意這個觀點。其實這個仍是要看團隊的積累的,若是團隊你們熟悉這套架構體系,玩轉微服務多年的話,那麼其實不少問題會在編碼的過程當中直接考慮進去,不少時候設計也能夠認爲是一個熟能生巧的活,作了多了天然知道什麼東西應該放在哪裏,怎麼去分怎麼去合,因此並不會有太多的額外時間成本。這三駕馬車構成的這麼一套簡單實用的架構方案我認爲能夠適用於大多數的互聯網項目,只是有些互聯網項目會更偏重其中的某一方面弱化另外一方面,但願本文對你有用。