免费视频淫片aa毛片_日韩高清在线亚洲专区vr_日韩大片免费观看视频播放_亚洲欧美国产精品完整版

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
互聯(lián)網(wǎng)架構(gòu):屢試不爽的架構(gòu)三馬車

這里所說的三架馬車是指微服務(wù)消息隊(duì)列定時(shí)任務(wù)。如下圖所示,這里是一個(gè)三駕馬車共同驅(qū)動(dòng)的一個(gè)立體的互聯(lián)網(wǎng)項(xiàng)目的架構(gòu)。不管項(xiàng)目是大是小,這個(gè)架構(gòu)模板的形態(tài)一旦定型了之后就不太會變,區(qū)別只是我們有更多的服務(wù)有更復(fù)雜的調(diào)用,更復(fù)雜的消息流轉(zhuǎn),更多的Job,整個(gè)架構(gòu)整體是可擴(kuò)展的,而且不會變形,這個(gè)架構(gòu)可以在很長的一段時(shí)間內(nèi)無需有大的調(diào)整。

圖上畫了虛線框的都代表這個(gè)模塊或項(xiàng)目是不包含太多業(yè)務(wù)邏輯的,純粹是一層皮(會調(diào)用服務(wù)但是不會觸碰數(shù)據(jù)庫)。黑色線的箭頭代表依賴關(guān)系,綠色和紅色箭頭分別是MQ的發(fā)送和訂閱消息流的方向。具體在后文都會進(jìn)一步詳細(xì)說明。

微服務(wù)

微服務(wù)并不是一個(gè)很新的概念,在10年前的時(shí)候我就開始實(shí)踐這個(gè)架構(gòu)風(fēng)格,在四個(gè)公司的項(xiàng)目中全面實(shí)現(xiàn)了微服務(wù),越來越堅(jiān)信這是非常適合互聯(lián)網(wǎng)項(xiàng)目的一個(gè)架構(gòu)風(fēng)格。不是說我們的服務(wù)一定要跨物理機(jī)器進(jìn)行遠(yuǎn)程調(diào)用,而是我們通過進(jìn)行有意的設(shè)計(jì)讓我們的業(yè)務(wù)在一開始的時(shí)候就按照領(lǐng)域進(jìn)行分割,這能讓我們對業(yè)務(wù)有更充分的理解,能讓我們在之后的迭代中輕易在不同的業(yè)務(wù)模塊上進(jìn)行耕耘,能讓我們的項(xiàng)目開發(fā)越來越輕松,輕松來源于幾個(gè)方面:

1. 如果我們能進(jìn)行微服務(wù)化,那么我們一定事先經(jīng)過比較完善的產(chǎn)品需求討論和領(lǐng)域劃分,每一個(gè)服務(wù)精心設(shè)計(jì)自己領(lǐng)域內(nèi)的表結(jié)構(gòu),這是一個(gè)很重要的設(shè)計(jì)過程,也決定了整個(gè)技術(shù)架構(gòu)和產(chǎn)品架構(gòu)是匹配的,對于All-In-One的架構(gòu)往往會省略這一過程,需求到哪里代碼寫到哪里。

2. 我們對服務(wù)的劃分和職責(zé)的定位如果是清晰的,對于新的需求,我們就能知道需要在哪里改怎么樣的代碼,沒有復(fù)制粘貼的存在少了很多坑。

3. 我們大多數(shù)的業(yè)務(wù)邏輯已經(jīng)開發(fā)完畢,直接重用即可,我們的新業(yè)務(wù)只是現(xiàn)有邏輯的聚合。在PRD評審后,開發(fā)得到的結(jié)論是只需要組合分別調(diào)用ABC三個(gè)服務(wù)的XYZ方法,然后在C服務(wù)中修改一下Z方法增加一個(gè)分支邏輯,就可以構(gòu)建起新的邏輯,這種爽快的感覺難以想象。

4. 在性能存在明顯瓶頸的時(shí)候,我們可以針對性地對某些服務(wù)增加更多機(jī)器進(jìn)行擴(kuò)容,而且因?yàn)榉?wù)的劃分,我們更清楚系統(tǒng)的瓶頸所在,從10000行代碼定位到一行性能存在問題的代碼是比較困難的,但是如果這10000行代碼已經(jīng)是由10個(gè)服務(wù)構(gòu)成的,那么先定位到某個(gè)服務(wù)存在性能問題然后再針對這個(gè)服務(wù)進(jìn)行分析一下子降低了定位問題的復(fù)雜度。

5. 如果業(yè)務(wù)有比較大的變動(dòng)需要下線,那么我們可以肯定的是底層的公共服務(wù)是不會淘汰的,下線對應(yīng)業(yè)務(wù)的聚合業(yè)務(wù)服務(wù)停掉流量入口,然后下線相關(guān)涉及到的基礎(chǔ)服務(wù)進(jìn)行部分接口即可。如果擁有完善的服務(wù)治理平臺,整個(gè)操作甚至無需改動(dòng)代碼。

這里也要求我們做到幾個(gè)方面的原則:

1. 服務(wù)的粒度劃分需要把控好。我的習(xí)慣是先按照領(lǐng)域來分不會錯(cuò),隨著項(xiàng)目的進(jìn)展慢慢進(jìn)行更細(xì)粒度的拆分。比如對于互聯(lián)網(wǎng)金融P2P業(yè)務(wù),一開始可以分為:

  • a 三方合作服務(wù)PartnerInvestService:對接合作的三方理財(cái)平臺的流量

  • b 普通投資服務(wù)NormalInvestService:最普通形態(tài)的資產(chǎn)的主流程

  • c 預(yù)約投資產(chǎn)品服務(wù)ReserveInvestService:需要預(yù)約投資的資產(chǎn)的主流程

  • d 周期性計(jì)劃產(chǎn)品服務(wù)AutoInvestService:會定期自動(dòng)復(fù)投的理財(cái)產(chǎn)品主流程

  • e 投資人交易服務(wù)TradeService:專門負(fù)責(zé)處理投資人的交易行為,比如投資

  • f 借款人交易服務(wù)LoanService:專門負(fù)責(zé)處理借款人的交易行為,比如還款

  • g 用戶服務(wù)UserService:處理用戶的注冊登錄等

  • h 資產(chǎn)服務(wù)ProjectService:處理資產(chǎn)和標(biāo)的相關(guān)

  • i 賬戶賬務(wù)服務(wù)AccountService:處理用戶的賬戶各個(gè)子賬戶和賬務(wù)記錄

  • j 營銷活動(dòng)服務(wù)ActivityService:處理各種活動(dòng)、用戶的積分體系

  • k 會員體系服務(wù)VipService:處理用戶的會員成長體系

  • l 銀行存管服務(wù)BankService:專門用于對接銀行存管系統(tǒng)

  • m 電子簽章服務(wù)DigSignService:專門用于對接三方數(shù)字簽章系統(tǒng)

  • n 消息推送服務(wù)MessageService:專門用于對接三方短信通道和推送SDK

2. 服務(wù)一定是立體的,不是在一個(gè)層次上的,如上圖,我們的服務(wù)有三個(gè)層次: 

聚合業(yè)務(wù)服務(wù):高層次的串起來整個(gè)流程的具有完整業(yè)務(wù)形態(tài)的業(yè)務(wù)服務(wù)。和基礎(chǔ)業(yè)務(wù)服務(wù)不同的是,這里是在完整描述一方面的業(yè)務(wù),這個(gè)業(yè)務(wù)往往是由各種基礎(chǔ)業(yè)務(wù)拼裝組合起來的。和不同外部合作方的不同合作形式,給用戶提供的產(chǎn)品的不同服務(wù)形態(tài),都決定了聚合業(yè)務(wù)服務(wù)會有業(yè)務(wù)流程上的差異化,如果把此類服務(wù)下放到基礎(chǔ)業(yè)務(wù)服務(wù)中,那么基礎(chǔ)業(yè)務(wù)服務(wù)會有各種if-else邏輯(根據(jù)產(chǎn)品類型、用戶類型進(jìn)行各種if-else),隨著業(yè)務(wù)的合作不合作,需求變動(dòng),基礎(chǔ)業(yè)務(wù)服務(wù)會腐化得很厲害,為了避免這個(gè)情況,我們把變動(dòng)的多的聚合業(yè)務(wù)邏輯放到獨(dú)立的業(yè)務(wù)服務(wù)中。一般而言,聚合業(yè)務(wù)服務(wù)因?yàn)榇砹霜?dú)立的業(yè)務(wù)流程,它們之間是不會進(jìn)行相互調(diào)用的,但是它們一定會調(diào)用大量的各類基礎(chǔ)業(yè)務(wù)服務(wù)。在第一點(diǎn)里說的標(biāo)有藍(lán)色字體的a~d這些服務(wù)都是此類服務(wù)。這個(gè)層次的服務(wù)的業(yè)務(wù)邏輯更多是在表達(dá)業(yè)務(wù)流程的復(fù)雜性和差異性,不會涉及到具體怎么處理賬戶信息、賬務(wù)信息、用戶信息,不會涉及到怎么處理具體的投資人和借款人的交易。比如對于預(yù)約這類業(yè)務(wù)形態(tài),它關(guān)注的是先要預(yù)約資產(chǎn),然后再由系統(tǒng)進(jìn)行自動(dòng)投資,底層完全依賴于投資人交易服務(wù)來做整個(gè)交易的過程。

基礎(chǔ)業(yè)務(wù)服務(wù):某一個(gè)領(lǐng)域業(yè)務(wù)相關(guān)的服務(wù)。此類服務(wù)之間是允許相互調(diào)用的,比如投資人交易服務(wù)和借款人交易服務(wù)免不了需要和用戶服務(wù)、資產(chǎn)服務(wù)、賬戶賬務(wù)服務(wù)進(jìn)行通訊做相關(guān)的用戶信息查詢、標(biāo)的信息查詢、記賬等業(yè)務(wù)操作。之所以投資人交易服務(wù)和借款人交易服務(wù)定位為基礎(chǔ)業(yè)務(wù)服務(wù)是因?yàn)椋鼈兲幚淼氖沁€是某一個(gè)具體方面的業(yè)務(wù),并不是全流程,在這個(gè)抽象層次上,業(yè)務(wù)不是那么容易變動(dòng)的,對于復(fù)雜的各種業(yè)務(wù)形態(tài)(比如預(yù)約交易、自動(dòng)復(fù)投交易、等額本息交易)會在這些服務(wù)之上形成聚合業(yè)務(wù)服務(wù)。在第一點(diǎn)里說的標(biāo)有綠色字體的e~k這些服務(wù)都是此類服務(wù)。在這個(gè)層次的服務(wù)雖然擁有大量的業(yè)務(wù)邏輯,但是其實(shí)已經(jīng)享受到了很大層度的公共基礎(chǔ)服務(wù)的重用了,而且和自己業(yè)務(wù)耦合較弱的額外邏輯往往沒有在本服務(wù)中堆積,由更多專職的基礎(chǔ)業(yè)務(wù)服務(wù)來承擔(dān)了這部分邏輯。

公共基礎(chǔ)服務(wù):負(fù)責(zé)某一個(gè)方面的基礎(chǔ)業(yè)務(wù)(沒有什么領(lǐng)域業(yè)務(wù)邏輯在里面),可以是自治的處理某一個(gè)方面的基礎(chǔ)業(yè)務(wù),也可以和外部通訊實(shí)現(xiàn)某一個(gè)方面的功能,服務(wù)之間是不會相互調(diào)用的,但是會被聚合業(yè)務(wù)服務(wù)和基礎(chǔ)業(yè)務(wù)服務(wù)調(diào)用。在第一點(diǎn)里說的標(biāo)有橙色字體的l~n這些服務(wù)都是此類服務(wù)。如果以后和外部的合作有變動(dòng),因?yàn)槲覀円呀?jīng)定義了對外的服務(wù)契約,可以輕易替換這個(gè)服務(wù)來更換合作的第三方,系統(tǒng)其余的地方幾乎不需要修改。所有的三方對接都建議獨(dú)立出公共基礎(chǔ)服務(wù),如果同一個(gè)業(yè)務(wù)對接多個(gè)三方渠道,比如推送對接了極光和個(gè)推,甚至公共基礎(chǔ)服務(wù)還可以由一個(gè)抽象聚合的推送服務(wù),下面再路由到具體的極光推送和個(gè)推推送服務(wù)。

希望在這里把這個(gè)事情說清楚了,怎么來劃分服務(wù)怎么劃分三個(gè)層次的服務(wù)是一個(gè)很有意思很有必要的事情,在服務(wù)劃分之后最好有一個(gè)明確的文檔來描述每一個(gè)服務(wù)的職責(zé),這樣我們在無需閱讀API的情況下可以大概定位到業(yè)務(wù)所在的服務(wù),整個(gè)復(fù)雜的系統(tǒng)就變得很直白了。

3.每一個(gè)服務(wù)對接的底層數(shù)據(jù)表是獨(dú)立的沒有交叉關(guān)聯(lián)的,也就是數(shù)據(jù)結(jié)構(gòu)是不直接對外的,需要使用其他服務(wù)的數(shù)據(jù)一定通過訪問接口進(jìn)行。好處也就是面向?qū)ο笤O(shè)計(jì)中封裝的好處:

  • 可以很方便地重構(gòu)底層的數(shù)據(jù)結(jié)構(gòu)甚至是數(shù)據(jù)源,只要接口不變,外部不會感知到。

  • 性能有問題的情況下需要加緩存、分表、拆庫、歸檔是比較方便的事情,畢竟數(shù)據(jù)源沒有外部依賴。 

說白了就是我的數(shù)據(jù)我做主,我想怎么搞外面管不著,在重構(gòu)或是做一些高層次技術(shù)架構(gòu)(比如異地多活)的時(shí)候,沒有底層數(shù)據(jù)被依賴,這太重要了。當(dāng)然,壞處或是麻煩的地方就是跨服務(wù)的調(diào)用使得數(shù)據(jù)操作無法在一個(gè)數(shù)據(jù)庫事務(wù)中完成,這并不是什么大問題,一是因?yàn)槲覀冞@種拆分方式并不會讓粒度太細(xì),大部分的業(yè)務(wù)邏輯是在一個(gè)業(yè)務(wù)服務(wù)里完成的,二是后面會提到跨服務(wù)的調(diào)用不管是通過MQ進(jìn)行的還是直接調(diào)用進(jìn)行的,都會有補(bǔ)償來實(shí)現(xiàn)最終一致性。

4.考慮到跨機(jī)器跨進(jìn)程調(diào)用服務(wù)穩(wěn)定性方面的顯著差異。在方法內(nèi)部進(jìn)行方法調(diào)用,我們需要考慮調(diào)用出現(xiàn)異常的情況,但是幾乎不需要考慮超時(shí)的情況,幾乎不需要考慮請求丟失的情況,幾乎不需要考慮重復(fù)調(diào)用的情況,對于遠(yuǎn)程服務(wù)調(diào)用,這些點(diǎn)都需要去重點(diǎn)考慮,否則系統(tǒng)整體就是基本可用,測試環(huán)境不出問題,但是到了線上問題百出的狀態(tài)。這就要求對于每一個(gè)服務(wù)的提供和調(diào)用多問幾個(gè)上面的問題,細(xì)細(xì)考慮到因?yàn)榫W(wǎng)絡(luò)問題方法沒有執(zhí)行多次執(zhí)行或部分執(zhí)行的情況:

  • 我們在對外提供服務(wù)的時(shí)候,不但要告知用戶服務(wù)提供的業(yè)務(wù)能力,還要告知用戶服務(wù)的特性,比如是否是冪等的(對于訂單類型的操作服務(wù),相同的訂單相同的操作強(qiáng)烈建議是冪等的,這樣調(diào)用方可以放心進(jìn)行重試或補(bǔ)償);是否需要外部進(jìn)行補(bǔ)償(在這里你可能說為什么需要外部進(jìn)行補(bǔ)償,服務(wù)就不能自己補(bǔ)償嗎,對于內(nèi)部的子邏輯服務(wù)當(dāng)然可以自己補(bǔ)償,但是有的時(shí)候因?yàn)榫W(wǎng)絡(luò)原因請求就沒有到服務(wù)端,服務(wù)端一無所知這個(gè)調(diào)用當(dāng)然無從去補(bǔ)償);是否有頻控的限制;是否有權(quán)限的限制;降級后的處理方式等等。

  • 反過來,我們調(diào)用其它服務(wù)也需要多問幾句目標(biāo)服務(wù)的特性,針對性進(jìn)行設(shè)計(jì)相應(yīng)的補(bǔ)償邏輯、一致性處理邏輯和降級邏輯。我們必須考慮到有些時(shí)候并不是服務(wù)端的問題,而是請求根本沒有到達(dá)服務(wù)端。

  • 服務(wù)本身往往也會有復(fù)雜的邏輯,作為客戶端的身份調(diào)用大量外部的服務(wù),所以服務(wù)端和客戶端的角色不是固定不變的,當(dāng)我們的服務(wù)內(nèi)部有許多客戶端來調(diào)用服務(wù)端的時(shí)候,對于每一個(gè)子邏輯我們都需要仔細(xì)考慮每一個(gè)環(huán)節(jié)。否正會出現(xiàn)的情況就是,這個(gè)服務(wù)是部分邏輯冪等的或是部分邏輯是具備最終一致性的。

如果你說,這么多服務(wù),我在實(shí)現(xiàn)的時(shí)候很難考慮到這些點(diǎn),我完全不去考慮分布式事務(wù)、冪等性、補(bǔ)償(毫不夸張地說,有的時(shí)候我們花了20%的時(shí)間實(shí)現(xiàn)了業(yè)務(wù)邏輯,然后花80%的時(shí)間在實(shí)現(xiàn)這些可靠性方面的外圍邏輯),行不行?也不是不可以,那么業(yè)務(wù)在線上跑的時(shí)候一定會是千瘡百孔的,如果整個(gè)業(yè)務(wù)的處理對可靠性方面的要求不高或是業(yè)務(wù)不面向用戶不會受到投訴的話,這部分業(yè)務(wù)的是可以暫時(shí)不考慮這些點(diǎn),但是諸如訂單業(yè)務(wù)這種核心的不允許有不一致性的業(yè)務(wù)還是需要全面考慮這些點(diǎn)的。

5. 考慮到跨機(jī)器跨進(jìn)程調(diào)用服務(wù)數(shù)據(jù)傳輸方面的顯著差異。對于本地的方法調(diào)用,如果參數(shù)和返回值傳的是對象,那么對于大部分的語言來說,傳的是指針(或指針的拷貝),指針指向的是堆中分配的對象,對象在數(shù)據(jù)傳輸上的成本幾乎忽略不計(jì),也沒有序列化和反序列化的開銷。對于跨進(jìn)程的服務(wù)調(diào)用,這個(gè)成本往往不能忽略不計(jì)。如果我們需要返回很多數(shù)據(jù),往往接口的定義需要進(jìn)行特殊的改造:

  • 通過使用分頁的形式,一次返回固定的少量數(shù)據(jù),客戶端按需拉取更多數(shù)據(jù)。

  • 可以在參數(shù)中傳類似于EnumSet的數(shù)據(jù)結(jié)構(gòu),讓客戶端告知服務(wù)端我需要什么層次的數(shù)據(jù),比如GetUserInfo接口可以提供給客戶端BasicInfo、VIPInfo、InvestData、RechargeData、WithdrawData,客戶端可以按需從服務(wù)端拿BasicInfo|VipInfo。

6. 這里還引申出方法粒度的問題,比如我們可以定義GetUserInfo通過傳入不用的參數(shù)來返回不同的數(shù)據(jù)組合,也可以分別定義GetUserBasicInfo、GetUserVIPInfo、GetUserInvestData等等細(xì)粒度的接口,接口的粒度定義取決于使用者會怎么來使用數(shù)據(jù),更趨向于一次使用單種類型數(shù)據(jù)還是復(fù)合類型的數(shù)據(jù)等等。

7. 然后我們需要考慮接口升級的問題,接口的改動(dòng)最好是兼容之前的接口,如果接口需要淘汰下線,需要先確保調(diào)用方改造到了新接口,確保調(diào)用方流量為0觀察一段時(shí)間后方能從代碼下線老接口。一旦服務(wù)公開出去,要進(jìn)行接口定義調(diào)整甚至下線往往就沒有這么容易了,不是自己說了算了。所以對外API的設(shè)計(jì)需要慎重點(diǎn)。

8. 最后不得不說,在整個(gè)公司都搞起了微服務(wù)后,跨部門的一些服務(wù)調(diào)用在商定API的時(shí)候難免會有一些扯皮的現(xiàn)象發(fā)生,到底是我傳給你呢還是你自己來拉,這個(gè)數(shù)據(jù)對我沒用為什么要在我這里留一下呢?拋開非技術(shù)層面的事情不說,這些扯皮也是有一些技術(shù)手段來化解的:

  • 明確服務(wù)職責(zé),也就明確了服務(wù)應(yīng)該感知到什么不應(yīng)該感知到什么。

  • 跨部門的服務(wù)交互的接口定義可以定的很輕,采用只有一個(gè)訂單號的接口或MQ通知+數(shù)據(jù)回拉的策略(誰數(shù)據(jù)多誰提供數(shù)據(jù)接口,不用把數(shù)據(jù)一次性推給下游)。

  • 數(shù)據(jù)提供方可以構(gòu)建一套通用數(shù)據(jù)接口,這樣可以滿足多個(gè)部門的需求,無需做定制化的處理。甚至在接口上可以提供落地和不落地兩種性質(zhì)的透傳。

你可能看到這里覺得很頭暈,為什么微服務(wù)需要額外考慮這么多東西,實(shí)現(xiàn)的復(fù)雜度一下子上升了。我想說的是我們需要換一個(gè)角度來考慮這個(gè)事情:

1. 我們不需要在一開始的時(shí)候?qū)λ羞壿嫸歼M(jìn)行嚴(yán)密的考慮,先覆蓋核心流程核心邏輯。因?yàn)榭绶?wù)成為了服務(wù)的提供方和使用方,相當(dāng)于除了我自己,還有很多其它人會來關(guān)系我的服務(wù)能力,大家會提出各種問題,這對設(shè)計(jì)一個(gè)可靠的方法是有好處的。

2. 即使在不跨服務(wù)調(diào)用的時(shí)候我們把所有邏輯堆積在一起,也不意味著這些邏輯一定是事務(wù)性的,實(shí)現(xiàn)嚴(yán)密的,跨服務(wù)調(diào)用往往是一定程度放大了問題產(chǎn)生的可能性。

3. 我們還有服務(wù)框架呢,服務(wù)框架往往會在監(jiān)控跟蹤層次和運(yùn)維系統(tǒng)結(jié)合在一起提供很多一體化的功能,這將封閉在內(nèi)部的方法邏輯打散暴露出來,對于有一個(gè)完善的監(jiān)控平臺的微服務(wù)系統(tǒng),在排查問題的時(shí)候你往往會感嘆這是一個(gè)遠(yuǎn)程服務(wù)調(diào)用就好了。

4. 最大的紅利還是之前說的,當(dāng)我們以清晰的業(yè)務(wù)邏輯形成了一個(gè)立體化的服務(wù)體系之后,任何需求可以解剖為很少量的代碼修改和一些組合的服務(wù)調(diào)用,而且你知道我這么做是不會有任何問題的,因?yàn)榈讓拥姆?wù)ABCDEFG都是經(jīng)過歷史考驗(yàn)的,這種爽快感體驗(yàn)過一次就會大呼過癮。

但是,如果服務(wù)粒度劃分的不合理,層次劃分的不合理,底層數(shù)據(jù)源有交叉,沒考慮到網(wǎng)絡(luò)調(diào)用失敗,沒考慮到數(shù)據(jù)量,接口定義不合理,版本升級過于魯莽,整個(gè)系統(tǒng)會出各種各樣的擴(kuò)展問題性能問題和Bug,這是很頭痛的,這也就需要我們有一個(gè)完善的服務(wù)框架來幫助我們定位各種不合理,在之后說到中間件的文章中會再具體著重介紹服務(wù)治理這塊。

消息隊(duì)列

消息隊(duì)列MQ的使用有下面幾個(gè)好處,或者說我們往往處于這些目的來考慮引入MQ:

1. 異步處理:類似于訂單這樣的流程一般可以定義出一個(gè)核心流程,這個(gè)流程用于處理核心訂單的狀態(tài)機(jī),需要盡快同步落庫完成,然后圍繞訂單會衍生出一系列和用戶相關(guān)的庫存相關(guān)的后續(xù)的業(yè)務(wù)處理,這些處理完全不需要卡在用戶點(diǎn)擊提交訂單的那剎那進(jìn)行處理。下單只是一個(gè)確認(rèn)合法受理訂單的過程,后續(xù)的很多事情都可以慢慢在幾十個(gè)模塊中進(jìn)行流轉(zhuǎn),這個(gè)流轉(zhuǎn)過程哪怕是消耗5分鐘,用戶也無需感受到。

2. 流量洪峰:互聯(lián)網(wǎng)項(xiàng)目的一個(gè)特點(diǎn)是有的時(shí)候會做一些toC的促銷,免不了有一些流量洪峰,如果我們引入了消息隊(duì)列在模塊之間作為緩沖,那么backend的服務(wù)可以以自己既有的舒服的頻次來被動(dòng)消耗數(shù)據(jù),不會被強(qiáng)壓的流量擊垮。當(dāng)然,做好監(jiān)控是必不可少的,下面再細(xì)說一下監(jiān)控。

3. 模塊解耦:隨著項(xiàng)目復(fù)雜度的上升,我們會有各種來源于項(xiàng)目內(nèi)部和外部的事件(用戶注冊登陸、投資、提現(xiàn)事件等),這些重要事件可能不斷有各種各樣的模塊(營銷模塊、活動(dòng)模塊)需要關(guān)心,核心業(yè)務(wù)系統(tǒng)去調(diào)用這些外部體系的模塊,讓整個(gè)系統(tǒng)在內(nèi)部糾纏在一起顯然是不合適的,這個(gè)時(shí)候通過MQ進(jìn)行解耦,讓各種各樣的事件在系統(tǒng)中進(jìn)行松耦合流轉(zhuǎn),模塊之間各司其職也相互沒有感知,這是比較適合的做法。

4. 消息群發(fā):有一些消息是會有多個(gè)接收者的,接收者的數(shù)量還是動(dòng)態(tài)的(類似指責(zé)鏈的性質(zhì)也是可能的),在這個(gè)時(shí)候如果上下游進(jìn)行一對多的耦合就會更麻煩,對于這種情況就更適用使用MQ進(jìn)行解耦了。上游只管發(fā)消息說現(xiàn)在發(fā)生了什么事情,下游不管有多少人關(guān)心這個(gè)消息,上游都是沒有感知的。

這些需求互聯(lián)網(wǎng)項(xiàng)目中基本都存在,所以消息隊(duì)列的使用是非常重要的一個(gè)架構(gòu)手段。在使用上有幾個(gè)注意點(diǎn):

1. 我更傾向于獨(dú)立一個(gè)專門的listener項(xiàng)目(而不是合并在server中)來專門做消息的監(jiān)聽,然后這個(gè)模塊其實(shí)沒有過多的邏輯,只是在收到了具體的消息之后調(diào)用對應(yīng)的service中的API進(jìn)行消息處理。listener是可以啟動(dòng)多份做一個(gè)負(fù)載均衡的(取決于具體使用的MQ產(chǎn)品),但是因?yàn)檫@里幾乎沒有什么壓力,不是100%必須。注意,不是所有的service都是需要有一個(gè)配到的listener項(xiàng)目的,大多數(shù)公共基礎(chǔ)服務(wù)因?yàn)楸旧砗塥?dú)立不需要感知到外部的其它業(yè)務(wù)事件,所以往往是沒有l(wèi)istener的,基礎(chǔ)業(yè)務(wù)服務(wù)也有一些是類似的原因不需要有l(wèi)istener。

2. 對于重要的MQ消息,應(yīng)當(dāng)配以相應(yīng)的補(bǔ)償線作為備份,在MQ集群一切正常作為補(bǔ)漏,在MQ集群癱瘓的時(shí)候作為后背。我在日千萬訂單的項(xiàng)目中使用過RabbitMQ,雖然QPS在幾百上千,遠(yuǎn)遠(yuǎn)低于RabbitMQ壓測下來能抗住的數(shù)萬QPS,但是整體上有那么十萬分之一的丟消息概率(我也用過阿里的RocketMQ,但是因?yàn)閱瘟枯^小目前沒有觀察到有類似的問題),這些丟掉的消息馬上會由補(bǔ)償線進(jìn)行處理了。在極端的情況下,RabbitMQ發(fā)生了整個(gè)集群宕機(jī),A服務(wù)發(fā)出的消息無法抵達(dá)B服務(wù)了,這個(gè)時(shí)候補(bǔ)償Job開始工作,定期從A服務(wù)批量拉取消息提供給B服務(wù),雖然消息處理是一批一批的,但是至少確保了消息可以正常處理。做好這套后備是非常重要的,因?yàn)槲覀儫o法確保中間件的可用性在100%。

3. 補(bǔ)償?shù)膶?shí)現(xiàn)是不帶任何業(yè)務(wù)邏輯的,我們再梳理一下補(bǔ)償這個(gè)事情。如果A服務(wù)是消息的提供者,B-listener是消息監(jiān)聽器,聽到消息后會調(diào)用B-server中具體的方法handleXXMessage(XXMessage message)來執(zhí)行業(yè)務(wù)邏輯,在MQ停止工作的時(shí)候,有一個(gè)Job(可配置補(bǔ)償時(shí)間以及每次拉取的量)來定期調(diào)用A服務(wù)提供的專有方法getXXMessages(LocalDateTime from, LocalDateTime to, int batchSize)來拉取消息,然后還是(可以并發(fā))調(diào)用B-server的那個(gè)handleXXMessage來處理消息。這個(gè)補(bǔ)償?shù)腏ob可以重用的可配置的,無需每次為每一個(gè)消息都手寫一套,唯一需要多做的事情是A服務(wù)需要提供一個(gè)拉取消息的接口。那你可能會說,我A服務(wù)這里還需要維護(hù)一套基于數(shù)據(jù)庫的消息隊(duì)列嗎,這個(gè)不是自己搞一套基于被動(dòng)拉的消息隊(duì)列了嗎?其實(shí)這里的消息往往只是一個(gè)轉(zhuǎn)化工作,A一定在數(shù)據(jù)庫中有落地過去一段時(shí)間發(fā)生過變動(dòng)的數(shù)據(jù),只要把這些數(shù)據(jù)轉(zhuǎn)化為Message對象提供出去即可。B-server的handleXXMessage由于是冪等的,所以無所謂消息是否重復(fù)處理,這里只是在應(yīng)急情況下進(jìn)行無腦的過去一段時(shí)間的數(shù)據(jù)的依次處理。

4. 所有消息的處理端最好對相同的消息處理實(shí)現(xiàn)冪等,即使有一些MQ產(chǎn)品支持消息處理且只處理一次,靠自己做好冪等能讓事情變得更簡單。

5. 有一些場景下有延遲消息或延遲消息隊(duì)列的需求,諸如RabbitMQ、RocketMQ都有不同的實(shí)現(xiàn)方式。

6. MQ消息一般而言有兩種,一種是(最好)只能被一個(gè)消費(fèi)者進(jìn)行消費(fèi)并且只消費(fèi)一次的,另一種是所有訂閱者都可以來處理,不限制人數(shù)。不用的MQ中間件對于這兩種形式都有不同的實(shí)現(xiàn),有的時(shí)候使用消息類型來做,有的使用不同的交換機(jī)來做,有的是使用group的劃分來做(不同的group可以重復(fù)消息相同的消息)。一般來說都是支持這兩種實(shí)現(xiàn)的。在使用具體產(chǎn)品的時(shí)候務(wù)必研究相關(guān)的文檔,做好實(shí)驗(yàn)確保這兩種消息是以正確的方式在處理,以免發(fā)生妖怪問題。

7. 需要做好消息監(jiān)控,最最重要的是監(jiān)控消息是否有堆積,有的話需要及時(shí)增強(qiáng)下游處理能力(加機(jī)器,加線程),當(dāng)然做的更好點(diǎn)可以以熱點(diǎn)拓?fù)鋱D繪制所有消息的流向流速一眼就可以看到目前哪些消息有壓力。你可能會想既然消息都在MQ體系中不會丟失,消息有堆積處理慢一點(diǎn)其實(shí)也沒什么問題。是的,消息可以有適當(dāng)?shù)亩逊e,但是不能大量堆積,如果MQ系統(tǒng)出現(xiàn)存儲問題,大量堆積的消息有丟失也是比較麻煩的,而且有一些業(yè)務(wù)系統(tǒng)對于消息的處理是看時(shí)間的,過晚到達(dá)的消息是會認(rèn)為業(yè)務(wù)違例進(jìn)行忽略的。

8. 圖上畫了兩個(gè)MQ集群,一套對內(nèi)一套對外。原因是對內(nèi)的MQ集群我們在權(quán)限上控制可以相對弱點(diǎn),對外的集群必須明確每一個(gè)Topic,而且Topic需要由固定的人來維護(hù)不能在集群上隨意增刪Topic造成混亂。對內(nèi)對外的消息實(shí)現(xiàn)硬隔離對于性能也有好處,建議在生產(chǎn)環(huán)境把對內(nèi)對外的MQ集群進(jìn)行隔離劃分。

 

定時(shí)任務(wù)

定時(shí)任務(wù)的需求有那么幾類:

1. 如之前所說,跨服務(wù)調(diào)用,MQ通知難免會有不可達(dá)的問題,我們需要有一定的機(jī)制進(jìn)行補(bǔ)償。

2. 有一些業(yè)務(wù)是基于任務(wù)表進(jìn)行驅(qū)動(dòng)的,有關(guān)任務(wù)表的設(shè)計(jì)下面會詳細(xì)說明。

3. 有一些業(yè)務(wù)是定時(shí)定期來進(jìn)行處理的,根本不需要實(shí)時(shí)進(jìn)行處理(比如通知用戶紅包即將過期,和銀行進(jìn)行日終對賬,給用戶出賬單等)。和2的區(qū)別在于,這里的任務(wù)的執(zhí)行時(shí)間和頻次是五花八門的,2的話一般而言是固定頻次的。

詳細(xì)說明一下任務(wù)驅(qū)動(dòng)是怎么一回事。其實(shí)在數(shù)據(jù)庫中做一些任務(wù)表,以這些表驅(qū)動(dòng)作為整個(gè)數(shù)據(jù)處理的核心體系,這套被動(dòng)的運(yùn)作方式是最最可靠的,比MQ驅(qū)動(dòng)或服務(wù)驅(qū)動(dòng)兩種形態(tài)可靠多,天生必然是可負(fù)載均衡的+冪等處理+補(bǔ)償?shù)降椎模蝿?wù)表可以設(shè)計(jì)下面的字段:

  • 自增ID

  • 任務(wù)類型:表明具體的任務(wù)類型,當(dāng)然也可以不同的任務(wù)類型直接做多個(gè)任務(wù)表。

  • 外部訂單號:和外部業(yè)務(wù)邏輯的唯一單號關(guān)聯(lián)起來。

  • 執(zhí)行狀態(tài):未處理(等待處理),處理中(防止被其它Job搶占),成功(最終成功了),失敗(暫時(shí)失敗,會繼續(xù)進(jìn)行重試),人工介入(永遠(yuǎn)不會再變了,一定需要人工處理,需要報(bào)警通知)

  • 重試次數(shù):處理過太多次還是失敗的可以歸類為死信,由專門的死信隊(duì)列任務(wù)單獨(dú)再進(jìn)行若干次的重試不行的話就報(bào)警人工干預(yù)

  • 處理歷史:每一次的處理結(jié)果,Json的List保存在這里供參考

  • 上次處理時(shí)間:最近一次執(zhí)行時(shí)間

  • 上次處理結(jié)果:最近一次執(zhí)行結(jié)果

  • 創(chuàng)建時(shí)間:數(shù)據(jù)庫維護(hù)

  • 最后修改時(shí)間:數(shù)據(jù)庫維護(hù)

除了這些字段之外,還可能會加一些業(yè)務(wù)自己的字段,比如訂單狀態(tài),用戶ID等等信息作為冗余。任務(wù)表可以進(jìn)行歸檔減少數(shù)據(jù)量,任務(wù)表扮演了消息隊(duì)列的性質(zhì),我們需要有監(jiān)控可以對數(shù)據(jù)積壓,出入隊(duì)不平衡處理不過來,死信數(shù)據(jù)發(fā)生等等情況進(jìn)行報(bào)警。如果我們的流程處理是任務(wù)ABCD順序來處理的話,每一個(gè)任務(wù)因?yàn)橛凶约旱臋z查間隔,這套體系可能會浪費(fèi)一點(diǎn)時(shí)間,沒有通過MQ實(shí)時(shí)串聯(lián)這么高效,但是我們要考慮到的是,任務(wù)的處理往往是批量數(shù)據(jù)獲取+并行執(zhí)行的,和MQ基于單條數(shù)據(jù)的處理是不一樣的,總體上來說吞吐上不會有太多的差異,差的只是單條數(shù)據(jù)的執(zhí)行時(shí)間,考慮到任務(wù)表驅(qū)動(dòng)執(zhí)行的被動(dòng)穩(wěn)定性,對于有的業(yè)務(wù)來說,這不失為一種選擇。

這里再說明一下Job的幾個(gè)設(shè)計(jì)原則:

1. Job可以由各種調(diào)度框架來驅(qū)動(dòng),比如ElasticJob、Quartz等等,需要獨(dú)立項(xiàng)目處理,不能和服務(wù)混在一起,部署啟動(dòng)多份往往會有問題。當(dāng)然,自己實(shí)現(xiàn)一個(gè)任務(wù)調(diào)度框架也不是很麻煩的事情,在執(zhí)行的時(shí)候來決定Job在哪臺機(jī)器來跑,讓整個(gè)集群的資源使用更合理。說白了就是兩種形態(tài),一種是Job部署在那里由框架來觸發(fā),還有就是只是代碼在那里,由框架來起進(jìn)程。

2. Job項(xiàng)目只是一層皮,最多有一些配置的整合,不應(yīng)該有實(shí)際的業(yè)務(wù)邏輯,不會觸碰數(shù)據(jù)庫,大部分情況就是在調(diào)用具體服務(wù)的API接口。Job項(xiàng)目就負(fù)責(zé)配置和頻次的控制。

3. 補(bǔ)償類的Job注意補(bǔ)償次數(shù),避免整個(gè)任務(wù)被死信數(shù)據(jù)卡住的問題。

三馬車都說完了,那么,最后我們來梳理一下這么一套架構(gòu)下整個(gè)項(xiàng)目的模塊劃分:

  • Site:

    • front

    • console

    • app-gateway

  • Fa?ade Service:

    • partnerinvestservice-api

    • partnerinvestservice-server

    • partnerinvestservice-listener

    • normalinvestservice-api

    • normalinvestservice-server

    • normalinvestservice-listener

    • reserveinvestservice-api

    • reserveinvestservice-server

    • reserveinvestservice-listener

    • autoinvestservice-api

    • autoinvestservice-server

    • autoinvestservice-listener

  • Business Service:

    • tradeservice-api

    • tradeservice-server

    • tradeservice-listener

    • loanservice-api

    • loanservice-server

    • loanservice-listener

    • userservice-api

    • userservice-server

    • projectservice-api

    • projectservice-server

    • accountservice-api

    • accountservice-server

    • accountservice-listener

    • activityservice-api

    • activityservice-server

    • activityservice-listener

    • vipservice-api

    • vipservice-server

    • vipservice-listener

  • Foundation Service:

    • bankservice-api

    • bankservice-server

    • digsignservice-api

    • digsignservice-server

    • messageservice-api

    • messageservice-server

  • Job:

    • scheduler-job

    • task-job

    • compensation-job

這每一個(gè)模塊都可以打包成獨(dú)立的包,所有的項(xiàng)目不一定都要在一個(gè)項(xiàng)目空間內(nèi),可以拆分為20個(gè)項(xiàng)目,服務(wù)的api+server+listener放在一個(gè)項(xiàng)目內(nèi),這樣其實(shí)有利于CICD缺點(diǎn)就是修改代碼的時(shí)候需要打開N個(gè)項(xiàng)目。

之前開篇的時(shí)候說過,使用這套簡單的架構(gòu)既能夠有很強(qiáng)的擴(kuò)展余地,復(fù)雜程度上或者說工作量上不會比All-In-One的架構(gòu)多多少,看到這里你可能覺得并不同意這個(gè)觀點(diǎn)。其實(shí)這個(gè)還是要看團(tuán)隊(duì)的積累的,如果團(tuán)隊(duì)大家熟悉這套架構(gòu)體系,玩轉(zhuǎn)微服務(wù)多年的話,那么其實(shí)很多問題會在編碼的過程中直接考慮進(jìn)去,很多時(shí)候設(shè)計(jì)也可以認(rèn)為是一個(gè)熟能生巧的活,做了多了自然知道什么東西應(yīng)該放在哪里,怎么去分怎么去合,所以并不會有太多的額外時(shí)間成本。這三駕馬車構(gòu)成的這么一套簡單實(shí)用的架構(gòu)方案我認(rèn)為可以適用于大多數(shù)的互聯(lián)網(wǎng)項(xiàng)目,只是有些互聯(lián)網(wǎng)項(xiàng)目會更偏重其中的某一方面弱化另一方面,希望本文對你有用。

本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊舉報(bào)。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
App客戶端架構(gòu)演化之路
配置中心,互聯(lián)網(wǎng)架構(gòu)解耦利器
首席架構(gòu)師是如何選擇架構(gòu)方案的?
阿里技術(shù)專家詳解DDD系列 第二彈
從MVC到DDD的架構(gòu)演進(jìn)
保存|DDD之代碼架構(gòu)
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服