我只是下了個訂單,鬼知道我在微服務(wù)里經(jīng)歷了什么
時間:2023-05-08 16:03:01 | 來源:網(wǎng)站運營
時間:2023-05-08 16:03:01 來源:網(wǎng)站運營
我只是下了個訂單,鬼知道我在微服務(wù)里經(jīng)歷了什么:
當(dāng)我傻啊,用戶在電商網(wǎng)站購買成功,還在微服務(wù)中,那肯定就是有一套微服務(wù)架構(gòu)的電商系統(tǒng)。
設(shè)計一套電商系統(tǒng)還不簡單
簡單想象一下,既然是一個電商系統(tǒng),有用戶去購買,就肯定得有一個
用戶模塊,購買什么東西總不是西北風(fēng)吧,購買肯定是商品吧,省掉購物車,就得有
商品模塊吧,商品總得有庫存吧,庫存就暫時跟商品放一起吧,什么倉儲物流先別管,就當(dāng)作是虛擬商品好了,反正題目也沒說不能是虛擬商品^_^,購買成功了,那就必須有訂單吧,加個
訂單模塊,下完單總得支付吧,不付錢人家憑什么把東西給你,那就得有個
支付模塊。
簡單粗暴,四個模塊
用戶模塊,商品模塊(庫存),訂單模塊,支付模塊
好,幾個模塊搞定,外加下單流程圖
等等,貌似題目說是微服務(wù),既然是微服務(wù)就涉及到拆分服務(wù)的問題
DDD 領(lǐng)域驅(qū)動設(shè)計
剛剛確實是梳理了一下模塊,既然是微服務(wù),就得進(jìn)行服務(wù)的拆分,服務(wù)怎么進(jìn)行拆分呢,貌似按照剛次梳理模塊來劃分也是可以,不過這樣好像顯得我很不是專業(yè),聽說現(xiàn)在很多人都要使用DDD(領(lǐng)域驅(qū)動設(shè)計)來指導(dǎo)微服務(wù)的拆分。
參考DDD的設(shè)計,DDD官方的架構(gòu)草圖,總體架構(gòu)分為四層,Infrastructure(基礎(chǔ)實施層),Domain(領(lǐng)域?qū)?,Application(應(yīng)用層),Interfaces(表示層,也叫用戶界面層或是接口層)
微服務(wù)結(jié)合DDD
不過對于領(lǐng)域設(shè)計而言代碼層其實不是最重要,最要的是如何去劃分領(lǐng)域,劃分好邊界。而對于微服務(wù)而言,非常適合從業(yè)務(wù)上去劃分各個Modules,劃分好各個業(yè)務(wù)板塊,微服務(wù) + DDD,個人覺得首先從微服務(wù)的角度考慮去劃分大的業(yè)務(wù)模塊,每個微服務(wù)都應(yīng)該是一個可以獨立部署,各司其職的模塊。簡單的說,在微服務(wù)實際的開發(fā)中,結(jié)合DDD的思想去劃分所有屬于自己的領(lǐng)域。
實施DDD的關(guān)鍵
第一點是使用通過的語言建立所有的聚合,實體,值對象。
第二點也就是最關(guān)鍵的“建?!?br>
- 劃分“戰(zhàn)略建模”,從一個種宏觀的角度去審核整個項目,劃分出“界限上下文”,形成具有上帝視角的“上下文映射圖”
- 還有一個建模“戰(zhàn)術(shù)建?!保谖覀兊摹皯?zhàn)略建?!眲澐殖鰜淼摹敖缦奚舷挛摹狈N進(jìn)行“聚合”,“實體”,“值對象”,并按照模塊分組。
構(gòu)建我們電商系統(tǒng)的上下文映射圖
先來確定我們的戰(zhàn)略核心的領(lǐng)域是什么,我們的目的是什么,作為一個電商系統(tǒng),我們的核心肯定是賣出更多的商品,獲取更多訂單更多的利潤,那么銷售可以作為我們的一個核心的領(lǐng)域。這個作為一個明確核心域確立下來。
確定完核心子域后,根據(jù)對這個領(lǐng)域的理解劃分出各個上下文,然后根據(jù)上下文再確定其他的相關(guān)領(lǐng)域。
初步我們可以看出圍繞銷售核心域的包含的幾大塊內(nèi)容,價格,銷售方式,購買的方式,已經(jīng)購買。
然后我們對支撐著核心域的子域也做了劃分,支撐著核心域的有商品域,用戶域,通用域有訂單域,物流域,支付域。
回到我們的主題,我們這次沒有購物車,也沒有各個會員銷售價格,把一些上下文拿掉,并建立映射。
領(lǐng)域驅(qū)動設(shè)計看似簡單,其實很難實施,因為在各個環(huán)節(jié)中都需要對應(yīng)的領(lǐng)域?qū)<业膮⒓踊蛑笇?dǎo),這樣才能設(shè)計出最符合實際的上下文映射圖,而且我們花費的精力可能相比以后的數(shù)據(jù)驅(qū)動開發(fā)模式更多,但在整體對項目的把控性能上說,領(lǐng)域比數(shù)據(jù)驅(qū)動更加抽象,更加的頂層設(shè)計,在對應(yīng)互聯(lián)網(wǎng)的多變情況看得更遠(yuǎn)。
我們將微服務(wù)拆分為5個領(lǐng)域,分別是銷售域,商品域,用戶域,訂單域,支付域。
完美,接下來就可以開始開發(fā)了 ^?_?^
等等,兵馬未動,糧草先行;代碼未動,圖先行,先把時序圖畫出來
時序圖
一個簡單的下單流程,涵蓋了幾個領(lǐng)域
完美,接下來就可以開發(fā)微服務(wù)了^?_?^
等等,微服務(wù)的技術(shù)棧還未選型
微服務(wù)技術(shù)棧選型
服務(wù)拆分完了,時序圖也畫完了,可以開始我們的微服務(wù)之旅了,目前主流的微服務(wù)有阿里大名鼎鼎的dubbo和Spring-Cloud全家桶,還有新浪的Motan。比較熟悉的還是dubbo和spring-cloud,也都使用過,究竟應(yīng)該選用哪一個呢?
因為之前都使用過,做點簡單,粗暴的總結(jié)。dubbo在很早之前就開始使用,當(dāng)時的微服務(wù)還沒有現(xiàn)在這么火,很多理論體系也未完善,dubbo更像是一套rpc整合框架,spring-cloud則更傾向微服務(wù)架構(gòu)的生態(tài)。相比Dubbo,springCloud可以說是微服務(wù)一整套的解決方案,在功能上是dubbo的一個超級。 Dubbo和SpringCloud比喻,Dubbo架構(gòu)的微服務(wù)就像組裝電腦,各個環(huán)節(jié)自由度很高。springCloud更像品牌機(jī)。
基于不折騰,簡單快捷,更傾向選擇spring-cloud,ok,就定下來技術(shù)棧使用spring-cloud,愉快的決定。
等等,就這么草率就決定用spring-cloud做為微服務(wù),難道不需要把微服務(wù)的利弊先弄清楚嗎?
微服務(wù) : 利和弊
既然選擇了微服務(wù),就得知道微服務(wù)的利和弊,特別是弊,引入了微服務(wù),就等于引入了一套復(fù)雜的體系,一套復(fù)雜的體系帶來的各種挑戰(zhàn)必須事先了解清楚。
利:
1.強(qiáng)模塊化邊界
我們知道做軟件架構(gòu),軟件設(shè)計,模塊化是非常重要的一點,一開始我們寫程序做軟件,我們采用類的方式來做模塊化,后面開始采用組件或類庫的方式做模塊化,可以做到工程上的重用和分享給其他團(tuán)隊來使用。微服務(wù)在組件的層次上面又高了一層,以服務(wù)的方式來做模塊化,每個團(tuán)隊獨立開始和維護(hù)自己的服務(wù),有明顯的一個邊界,開發(fā)完一個服務(wù)其他團(tuán)隊可以直接調(diào)用這個服務(wù),不需要像組件通過jar或源碼的方式去進(jìn)行分享,所以微服務(wù)的邊界是比較清晰的。
2.可獨立部署
3.技術(shù)多樣性
弊(或者說挑戰(zhàn)):
1.分布式復(fù)雜性
在原來單塊應(yīng)用就是一個應(yīng)用,一個對單塊應(yīng)用的架構(gòu)比較熟悉的人可以對整個單塊應(yīng)用有一個很好的把控。但是到了分布式系統(tǒng),微服務(wù)化了以后可能涉及到的服務(wù)有好幾十個,一些大公司可能涉及到的服務(wù)上百個,服務(wù)與服務(wù)之間是通過相互溝通來實現(xiàn)業(yè)務(wù),那么這個時候整個系統(tǒng)就變成非常復(fù)雜,一般的開發(fā)人員或一個團(tuán)隊都無法理解整個系統(tǒng)是如何工作的,這個就是分布式帶來的復(fù)雜性。
2.最終一致性
微服務(wù)的數(shù)據(jù)是分散式治理的,每個團(tuán)隊都有自己的數(shù)據(jù)源和數(shù)據(jù)拷貝,比方說團(tuán)隊A有訂單數(shù)據(jù),B團(tuán)隊也有訂單數(shù)據(jù),團(tuán)隊A修改了訂單數(shù)據(jù)是否應(yīng)該同步給團(tuán)隊B的數(shù)據(jù)呢,這里就涉及到數(shù)據(jù)一致性問題,如果沒有很好的解決一致性問題,就可能造成數(shù)據(jù)的不一致,這個在業(yè)務(wù)上是不可以接受的。
3.運維復(fù)雜性
以往的運維需要管理的是機(jī)器+單塊的應(yīng)用,分布式系統(tǒng)和單塊應(yīng)用不一樣的是,分布式系統(tǒng)需要很多的服務(wù),服務(wù)與服務(wù)之間相互協(xié)同,那么對分布式系統(tǒng)的資源,容量規(guī)劃,對監(jiān)控,對整個系統(tǒng)的可靠性穩(wěn)定性都非常具備挑戰(zhàn)的。
只有在清楚了解微服務(wù)帶來的挑戰(zhàn),明知道山有虎偏向虎山行,才能夠真正的勝任挑戰(zhàn),最重要的是,要
清楚明了里面有什么坑,這么避免踩坑。
完美,已經(jīng)了解微服務(wù)帶來的好處和挑戰(zhàn),接下來就可以開始開發(fā)了 ^?_?^
等等,微服務(wù)還沒有做邏輯分層
微服務(wù)怎么做邏輯分層
目前我們的微服務(wù)里面有幾個服務(wù),分別是訂單,商品,用戶,如果客戶端向查看 “我的訂單” 這么一個接口, 如果客戶端假定是pc端,就需要請求三次接口,分別對接訂單,商品,用戶三個服務(wù),分別拿完三次調(diào)用數(shù)據(jù),再將三次調(diào)用數(shù)據(jù)進(jìn)行整合輸出展示。要知道pc調(diào)用后端服務(wù)是走外網(wǎng),這無疑大大增加了網(wǎng)絡(luò)的開銷,而且讓pc端變成更為復(fù)雜。假定在中間加多一個層為聚合服務(wù)層,即對網(wǎng)絡(luò)開銷進(jìn)行減少,因為微服務(wù)內(nèi)部是通過內(nèi)網(wǎng)進(jìn)行數(shù)據(jù)傳輸,也讓pc端的業(yè)務(wù)變得比較簡單。
圖中的 “pc聚合服務(wù)” 也是一個微服務(wù),只不過它是屬于聚合服務(wù)中間層,我們將為微服務(wù)進(jìn)行邏輯劃分,分為2個層:
微服務(wù)基礎(chǔ)服務(wù)層
基礎(chǔ)服務(wù)一般屬于互聯(lián)網(wǎng)平臺基礎(chǔ)性的支撐服務(wù),比方說,電商網(wǎng)站的基礎(chǔ)服務(wù)有訂單服務(wù),商品服務(wù),用戶服務(wù)等,這些都屬于比較基礎(chǔ)和原子性,下沉一個公司的基礎(chǔ)設(shè)施的低層,向下承接存儲,向上提供業(yè)務(wù)能力,有些公司叫(基礎(chǔ)服務(wù),中間層服務(wù),公共服務(wù)),netflix成為中間層服務(wù)。我們暫且統(tǒng)稱為基礎(chǔ)服務(wù)。
微服務(wù)聚合服務(wù)層
已經(jīng)有了基礎(chǔ)服務(wù)能提供業(yè)務(wù)能力,為什么還需要聚合服務(wù),因為我們有不同的接入端,如app和H5,pc等等,它們看似調(diào)用大致相同的數(shù)據(jù),但其實存在很多差異,例如PC需要展示更多信息,APP需要做信息裁剪等等。一般低層服務(wù)都是比較通用的,基礎(chǔ)服務(wù)應(yīng)該對外輸出相對統(tǒng)一的服務(wù),在抽象上做得比較好。但是對不同的外界app和pc的接入,我們需要作出不同的適配,這個時候需要有一個層去做出聚合裁剪的工作。例如一個商品詳情在pc端展示和app端的展示,pc可能會展示更多的信息,而app則需要對信息作出一些裁剪,如果基礎(chǔ)服務(wù)直接開放接口給到pc和app,那么基礎(chǔ)服務(wù)也需要去做成各種設(shè)配,這個很不利于基礎(chǔ)服務(wù)的抽象,所以我們在基礎(chǔ)層之上加入聚合服務(wù)層,這個層可以針對pc和app做成適當(dāng)?shù)脑O(shè)配進(jìn)行相應(yīng)的裁剪。
那么我們的微服務(wù)中,又
增加了一個服務(wù),屬于聚合服務(wù)。
好了,接下來可以愉快的coding…
等等,貌似不對,如果是單塊應(yīng)用加上事務(wù)應(yīng)該沒問題,這里是分布式,恐怕得考慮加分布式事務(wù)
分布式事務(wù)
我們來理一理創(chuàng)建訂單和扣件庫存模塊之間的關(guān)系
可以發(fā)現(xiàn),因為微服務(wù)的原因,我們把服務(wù)進(jìn)行了分布式,隨著各個數(shù)據(jù)庫也隨著變成分布式每個數(shù)據(jù)庫不一定存在相同的物理機(jī)中,那么這個時候單個數(shù)據(jù)庫的ACID已經(jīng)不能適應(yīng)這種情況,而在這種集群中想去保證集群的ACID幾乎很難達(dá)到,或者即使能達(dá)到那么效率和性能會大幅下降,最為關(guān)鍵的是再很難擴(kuò)展新的分區(qū)了,這個時候如果再追求集群的ACID會導(dǎo)致我們的系統(tǒng)變得很差,這時我們就需要引入一個新的理論原則來適應(yīng)這種集群的情況,就是 CAP
CAP定理
CAP 必須滿足一下的3個屬性:
– 一致性(C):在分布式系統(tǒng)中的所有數(shù)據(jù)備份,在同一時刻是否同樣的值。(等同于所有節(jié)點訪問同一份最新的數(shù)據(jù)副本)
– 可用性(A):在集群中一部分節(jié)點故障后,集群整體是否還能響應(yīng)客戶端的讀寫請求。(對數(shù)據(jù)更新具備高可用性)
– 分區(qū)容錯性(P):以實際效果而言,分區(qū)相當(dāng)于對通信的時限要求。系統(tǒng)如果不能在時限內(nèi)達(dá)成數(shù)據(jù)一致性,就意味著發(fā)生了分區(qū)的情況,必須就當(dāng)前操作在C和A之間做出選擇。
簡單的來說,在一個分布式系統(tǒng)中,最多能支持上面的兩種屬性。但顯然既然是分布式注定我們是必然要進(jìn)行分區(qū),既然分區(qū),我們就無法百分百避免分區(qū)的錯誤。因此,我們只能在一致性和可用性去作出選擇。
在分布式系統(tǒng)中,我們往往追求的是可用性,它的重要性比一致性要高,那么如何實現(xiàn)高可用,這里又有一個理論,就是BASE理論,它給CAP理論做了進(jìn)一步的擴(kuò)充。
BASE理論
BASE指出:
– Basically Available(基本可用)
– Soft state(軟狀態(tài))
– Eventually consistent(最終一致性)
BASE理論是對CAP中的一致性和可用性進(jìn)行一個權(quán)衡的結(jié)果,理論的核心思想就是:我們無法做到強(qiáng)一致,但每個應(yīng)用都可以根據(jù)自身的業(yè)務(wù)特點,采用適當(dāng)?shù)姆绞絹硎瓜到y(tǒng)達(dá)到最終一致性
好了,說了一大頓理論,程序員們都等急了,趕快來看看分布式事務(wù)的解決方案有哪些,可以進(jìn)行接下去的coding…
來吧,討論技術(shù)方案:
幾個方案拿出來了,因為我們不是專門來講解分布式事務(wù)的機(jī)制和原理,主要還是來做分布式事務(wù)的技術(shù)選型。
先排除掉我們應(yīng)該不會選擇的方案,一個是XA兩階段提交,這個在很多傳統(tǒng)型公司會被使用,但不適合互聯(lián)網(wǎng)微服務(wù)的分布式系統(tǒng),鎖定資源時間長,性能影響大,排除。
另一個是ali的GTS并沒有開源,目前已經(jīng)開源了fescar,不過目前善缺少調(diào)研,可能在下個階段研究后會使用,目前先排除。
剩下的是TCC和MQ消息事務(wù)兩種
MQ消息事務(wù)-RocketMQ
先說說MQ的分布式事務(wù),RocketMq在4.3版本已經(jīng)正式宣布支持分布式事務(wù),在選擇Rokcetmq做分布式事務(wù)請務(wù)必選擇4.3以上的版本。
事務(wù)消息作為一種異步確保型事務(wù), 將兩個事務(wù)分支通過 MQ 進(jìn)行異步解耦,RocketMQ 事務(wù)消息的設(shè)計流程同樣借鑒了兩階段提交理論,整體交互流程如下圖所示:
這個時候我們基本可以認(rèn)為,只有MQ發(fā)送方自己的本地事務(wù)執(zhí)行完畢,那么MQ的訂閱方必定百分百能夠接收到消息,我們再對下單減庫存的步驟進(jìn)行改造:
這里涉及到一個異步化的改造,我們理一下如果是同步流程中的各個步驟
- 查看商品詳情(或購物車)
- 計算商品價格和目前商品存在庫存(生成訂單詳情)
- 商品扣庫存(調(diào)用商品庫存服務(wù))
- 訂單確認(rèn)(生成有效訂單)
訂單創(chuàng)建完成后,發(fā)布一個事件“orderCreate” 到消息隊列中,然后由MQ轉(zhuǎn)發(fā)給訂閱該消息的服務(wù),因為是基于消息事務(wù),我們可以認(rèn)為訂閱該消息的商品模塊是百分百能收到這個消息的。
商品服務(wù)接受到orderCreate消息后就執(zhí)行扣減庫存的操作,注意,這里可能會有一些不可抗的因素導(dǎo)致扣減庫存失敗,無論成功或失敗,商品服務(wù)都將發(fā)送一個扣減庫存結(jié)果的消息“stroeReduce”到消息隊列中,訂單服務(wù)會訂閱扣減庫存的結(jié)果。
訂單服務(wù)收到消息后有兩種可能:
- 如果扣減庫存成功,將訂單狀態(tài)改為 “確認(rèn)訂單” ,下單成功
- 如果扣減庫存失敗,將訂單狀態(tài)改為 “失效訂單” ,下單失敗
這種模式將確認(rèn)訂單的流程變成異步化,
非常適合在高并發(fā)的使用,但是,切記了,這個需要前端用戶體驗的一些改變,要配合產(chǎn)品來涉及流程。
完美,使用MQ分布式事務(wù)就可以解決調(diào)一致性問題
等等,MQ消息事務(wù)方案的風(fēng)險了解一下
上面使用MQ的方式確實是可以完成A和B操作,但是A和B并不是嚴(yán)格一致性,而是最終一致性,我們犧牲掉嚴(yán)格一致性,換來性能的提升,這種很適合在大促高并發(fā)場景總使用,但是如果B一直執(zhí)行不成功,那么一致性也會被破壞,后續(xù)應(yīng)該考慮到更多的兜底方案,方案越細(xì)系統(tǒng)就將越復(fù)雜。
TCC方案
TCC是服務(wù)化的二階段變成模型,每個業(yè)務(wù)服務(wù)都必須實現(xiàn) try,confirm,calcel三個方法,這三個方式可以對應(yīng)到SQL事務(wù)中Lock,Commit,Rollback。
1). try階段
try只是一個初步的操作,進(jìn)行初步的確認(rèn),它的主要職責(zé)是完成所有業(yè)務(wù)的檢查,預(yù)留業(yè)務(wù)資源
2). confirm階段
confirm是在try階段檢查執(zhí)行完畢后,繼續(xù)執(zhí)行的確認(rèn)操作,必須滿足冪等性操作,如果confirm中執(zhí)行失敗,會有事務(wù)協(xié)調(diào)器觸發(fā)不斷的執(zhí)行,直到滿足為止
3). cancel是取消執(zhí)行,在try沒通過并釋放掉try階段預(yù)留的資源,也必須滿足冪等性,跟confirm一樣有可能被不斷執(zhí)行
接下來看看,我們的下單扣減庫存的流程怎么加入TCC
在try的時候,會讓庫存服務(wù)預(yù)留n個庫存給這個訂單使用,讓訂單服務(wù)產(chǎn)生一個“未確認(rèn)”訂單,同時產(chǎn)生這兩個預(yù)留的資源,
在confirm的時候,會使用在try預(yù)留的資源,在TCC事務(wù)機(jī)制中認(rèn)為,如果在try階段能正常預(yù)留的資源,那么在confirm一定能完整的提交
在try的時候,有任務(wù)一方為執(zhí)行失敗,則會執(zhí)行cancel的接口操作,將在try階段預(yù)留的資源進(jìn)行釋放。
完美,可以把我們的系統(tǒng)引入TCC ^?_?^
等等,有同學(xué)提問
- 有同學(xué)可能會問了,如果在confirm或cancel中,有一方的操作失敗了,可能出現(xiàn)異常等情況該怎么解決,這個就涉及TCC的事務(wù)協(xié)調(diào)器了,事務(wù)協(xié)調(diào)器就confirm或cancel沒有得到返回的時候,會啟用定時器不斷的進(jìn)行confirm或cancel的重試,這個也就是我們強(qiáng)調(diào),confirm,cancel接口必須是冪等性的一個原因了
- 還有同學(xué)會問了,為什么事務(wù)協(xié)調(diào)器知道confirm,或cancel沒有完成,這個就涉及到了TCC也做了一張本地消息表,會記錄一次事務(wù),包括主事務(wù),子事務(wù),事務(wù)的完成情況都會記錄在這種表中(當(dāng)然未必是表,可能是zk,redis等等介質(zhì)),然后啟用一個定時器去檢查這種表。
- 還有同學(xué)會問,事務(wù)怎么傳遞,這個就涉及使用的TCC的框架了,一般來說用的都是隱式傳參的方式。在主事務(wù)創(chuàng)建的時候用隱式傳參調(diào)用子事務(wù),子事務(wù)包含try,confirm,cancel都會記錄到事務(wù)表里面。
這里推薦TCC的開源框架使用mengyun的TCC,然后也可以其他的,無所謂。
完美,下單的流程開發(fā)完畢了,可以讓QA接入 ^?_?^
等等,微服務(wù)的保護(hù)措施做了嗎
熔斷限流隔離降級
微服務(wù)分布式依賴關(guān)系錯綜復(fù)雜,比方說前端的一個請求,這來到后端會被轉(zhuǎn)為為很多個請求,個時候后臺的服務(wù)出現(xiàn)不穩(wěn)定或者延遲,如果沒有好的限流熔斷措施,可能會造成用戶體驗的下降,嚴(yán)重的時候會出現(xiàn)雪崩效應(yīng),把整個網(wǎng)站給搞垮,如果向阿里巴巴在雙11等活動中,如果沒有一套好的限流熔斷措施,這是不可想象的,可能是根本無法支撐那么大的并發(fā)容量。
netflix在2012年前也沒有設(shè)計好的限流容錯,當(dāng)時也是飽受著系統(tǒng)穩(wěn)定性的困擾,好幾次網(wǎng)站因為沒有好的熔斷措施把網(wǎng)站搞垮,在2012年netflix啟動了彈性工程項目,其中有一個產(chǎn)品叫hystrix,這個產(chǎn)品主要用來解決微服務(wù)的可靠性,有了這個系統(tǒng)之后,netflix在系統(tǒng)穩(wěn)定性上上了一個大的臺階,在此之后就沒有出現(xiàn)過大規(guī)模的雪崩事故
下面使用hystrix也例子來講解一下限流熔斷
幾個概念:
熔斷,隔離,限流,降級,這幾個概念是分布式容錯最重要的概念和模式。
熔斷
如果說房子里面安裝了電路熔斷器,當(dāng)你使用超大功率的電路時,有熔斷設(shè)配幫你保護(hù)不至于出問題的時候把問題擴(kuò)大化。
隔離
我們知道計算資源都是有限的,cpu,內(nèi)存,隊列,線程池都是資源,他們都是限定的資源數(shù),如果不進(jìn)行隔離,一個服務(wù)的調(diào)用可能要消耗很多的線程資源,把其他服務(wù)的資源都給占用了,那么可能出現(xiàn)應(yīng)為一個服務(wù)的問題連帶效應(yīng)造成其他服務(wù)不能進(jìn)行訪問。
限流
讓大流量的訪問沖進(jìn)去我們的服務(wù)時,我們需要一定的限流措施,比方說我們規(guī)則一定時間內(nèi)只允許一定的訪問數(shù)從我們的資源過,如果再大的化系統(tǒng)會出現(xiàn)問題,那么就需要限流保護(hù)。
降級
如果說系統(tǒng)后題無法提供足夠的支撐能力,那么需要一個降級能力,保護(hù)系統(tǒng)不會被進(jìn)一步惡化,而且可以對用戶提供比較友好的柔性方案,例如告知用戶暫時無法訪問,請在一段時候后重試等等。
hystrix
hystrix就把上面說的 熔斷,隔離,限流,降級封裝在這么一個組件里面
下圖是hystrix內(nèi)部設(shè)計和調(diào)用流程
大致的工作流如下:
- 構(gòu)建一個HystrixCommand對象,用于封裝請求,并在構(gòu)造方法配置請求被執(zhí)行需要的參數(shù)
- 執(zhí)行命令,Hystrix提供了幾種執(zhí)行命令的方法,比較常用到的是synchrous和asynchrous
- 判斷電路是否被打開,如果被打開,直接進(jìn)入fallback方法
- 判斷線程池/隊列/信號量是否已經(jīng)滿,如果滿了,直接進(jìn)入fallback方法
- 執(zhí)行run方法,一般是HystrixCommand.run(),進(jìn)入實際的業(yè)務(wù)調(diào)用,執(zhí)行超時或者執(zhí)行失敗拋出未提前預(yù)計的異常時,直接進(jìn)入fallback方法
- 無論中間走到哪一步都會進(jìn)行上報metrics,統(tǒng)計出熔斷器的監(jiān)控指標(biāo)
- fallback方法也分實現(xiàn)和備用的環(huán)節(jié)
- 最后是返回請求響應(yīng)
完美,把hystrix加入我們系統(tǒng)吧,這樣突然有洪峰流量也不至于我們的系統(tǒng)一下就沖垮 ^?_?^
等等,hystrix的限流數(shù)值,錯誤數(shù)熔斷,超時熔斷,嘗試恢復(fù)比率這些需要我們配置的數(shù)值應(yīng)該怎么定呢?
這個就取決你的系統(tǒng)壓測的指標(biāo)和你部署的規(guī)模了,這里還涉及到一個容量設(shè)計的問題,一會我們將系統(tǒng)部署上線的時候再來詳細(xì)說道。
剛剛提到一個問題,就是這些限流數(shù)值,錯誤數(shù)熔斷這些數(shù)字,我們現(xiàn)在都寫在配置文件里面,例如說寫在properties,yml里面,當(dāng)有一天突然需要把限流數(shù)下調(diào)(可能是系統(tǒng)遭受到什么壓力打擊),那我們只能把代碼拉下來,巴拉巴拉改了,然后重新上傳打包,發(fā)布重啟,一個流程下來,不說個把小時吧,十來分鐘總少不了吧。
想辦法我們把這些配置項放到一個集中式配置中心
集中式配置中心
自己寫配置中心還挺麻煩的,去菜市場逛逛吧,菜市場里面有,springcloud-Config,百度的disconf,阿里的diamond,還有攜程的apollo
基本上他們的原理都差不多,配置中心可以簡單的理解為一個服務(wù)模塊,開發(fā)人員或運維人員可以通過界面對配置中心進(jìn)行配置,下面相關(guān)的微服務(wù)連接到配置中心上面就可以實時連接獲取到配置中心上面修改的參數(shù)。更新的方式一般有兩種
– pull模式,服務(wù)定時去拉取配置中心的數(shù)據(jù)
– push模式,服務(wù)一直連接到配置中心上,一旦配置有變成,配置中心將把變更的參數(shù)推送到對應(yīng)的微服務(wù)上
pull 和 push 兩種模式其實各有優(yōu)缺點。
- pull一般使用定時器拉取,就算某一個網(wǎng)絡(luò)抖動沒有pull成功,在下一次定時器的時候,終將能保證獲取最新的配置。
- push可以避免pull定時器存在的延時,基本可以做到實時獲取數(shù)據(jù),但也有問題就是網(wǎng)絡(luò)抖動的時候可能會丟失更新。
攜程的apollo
攜程的apollo比較有特色的是融合了pull和push兩種模式,把兩者的優(yōu)點進(jìn)行了結(jié)合,開發(fā)或運維人員在配置中心進(jìn)行修改,配置中心服務(wù)將實時將修改推送push到apollo的客戶端,但考慮到可能由于某些網(wǎng)絡(luò)抖動沒有推送成功,客戶端還具備了定時向apollo服務(wù)端拉取pull數(shù)據(jù)的功能,就算推送沒成功,但是只要一定時間周期,客戶端還是會主動去拉取同步數(shù)據(jù),保證能把最終配置同步到服務(wù)中。這個也是apollo在高可用方面上非常有特色的設(shè)計。
apollp在高可用上也做了保證,客戶端獲取到數(shù)據(jù)會把數(shù)據(jù)緩存在內(nèi)存,還會sync到本地磁盤,就算apollo服務(wù)器掛掉了,就算客戶端服務(wù)重啟了,也可以從本地磁盤中拉取回來數(shù)據(jù),繼續(xù)提供對外服務(wù),從這點來看apollo的配置中心在高可用上考慮還是比較周到的。
把配置中心配置上去后,我們就可以把hystrix還有mysql的用戶密碼,還有一些業(yè)務(wù)開關(guān)等等的配置參數(shù)放上去了。
完美,開發(fā)基本完工了,其實就幾個模塊,一個簡單的下單購物流程,當(dāng)我們把系統(tǒng)交付給運維,運維喊道,日志呢,做微服務(wù)怎么可以沒有調(diào)用鏈日志呢?
調(diào)用鏈監(jiān)控&日志
確實,微服務(wù)是一個分布式非常復(fù)雜系統(tǒng),如果沒有一套調(diào)用鏈監(jiān)控,如果服務(wù)之間依賴出現(xiàn)問題就很難進(jìn)行定位。
下圖是ali在鷹眼系統(tǒng)給出的微服務(wù)之“熵”
目前個大主流互聯(lián)網(wǎng)公司中,ali有非常出現(xiàn)的鷹眼系統(tǒng),點評也有一套很出名的調(diào)用鏈監(jiān)控系統(tǒng)CAT。調(diào)用鏈監(jiān)控其實最早是google提出來的,2010年google發(fā)表了一篇調(diào)用鏈的論文,論文以它內(nèi)部的調(diào)用鏈系統(tǒng)dapper命名,這個論文中講解調(diào)用鏈在google使用的經(jīng)驗和原理,大致的原理如下圖:
這里可以采用ELK的方式去記錄和展示調(diào)用鏈監(jiān)控日志,當(dāng)我們一條調(diào)用為一行記錄存儲下來
通過traceId 和 parentSpanId 就可以串聯(lián)起來為一個整體的鏈路,并可以從這個鏈路去分析錯誤或者調(diào)用延時和調(diào)用次數(shù)等等
目前市面主流的調(diào)用鏈選型有 zipkin,pinpoint,cat,skywalking,他們之間各有一些偏重點,值得一說的是skywalking國人出品的一款新的調(diào)用鏈工具,采用開源的基于字節(jié)碼注入的調(diào)用鏈分析,接入段無代碼入侵,而且開源支持多種插件,UI在幾款工具來說比較功能比較強(qiáng)大,而且ui也比較賞心悅目,目前已經(jīng)加入了apache孵化器。
采用了skywalking作為調(diào)用鏈工具
為何會采用skywaling,在低層原理的實現(xiàn),這幾款產(chǎn)品都差不多,但在實現(xiàn)和使用的細(xì)節(jié)相別還是很大。
- 首先在實現(xiàn)方式上,skywalking基本對于代碼做到了無入侵,采用java探針和字節(jié)碼增強(qiáng)的方式,而在cat還采用了代碼埋點,而zipkin采用了攔截請求,pinpoint也是使用java探針和字節(jié)碼增強(qiáng)。
- 其次在分析的顆粒度上,skywaling是方法級,而zipkin是接口級,其他兩款也是方法級。
- 在數(shù)據(jù)存儲上,skywalking可以采用日志體系中比較出名的ES,其他幾款,zipkin也可以使用ES,pinpoint使用Hbase,cat使用mysql或HDFS,相對復(fù)雜,由于目前公司對ES熟悉的人才比較有保證,選擇熟悉存儲方案也是考慮技術(shù)選型的重點。
- 還有就是性能影響,根據(jù)網(wǎng)上的一些性能報告,雖然未必百分百準(zhǔn)備,但也具備參考價值,skywalking的探針對吞吐量的影響在4者中間是最效的,經(jīng)過對skywalking的一些壓測也大致證明。
完美,把微服務(wù)的包打好,上傳到服務(wù)器就可以運行了 ^?_?^
等等,微服務(wù)包都打好了,剩下就是jar包或war包一個一個上傳到服務(wù)器上,然后用個腳本start,在以前單塊應(yīng)用還好,現(xiàn)在微服務(wù)幾十幾百個應(yīng)用,請問,運營人員怕不怕?
聽說,docker + kubernetes和微服務(wù)更配喔
docker + kubernetes
就幾個服務(wù),先不用容器化部署了…乍一看,沒玩沒了,還有CICD,灰度發(fā)布…容易編排…
下次再講把,先把服務(wù)部署上去吧
部署到生產(chǎn),預(yù)估容量
該把服務(wù)部署上線了,一個服務(wù)上線肯定得評估下或者預(yù)估下訪問量有多少用戶,有多少訪問,這個涉及到該配置多少的機(jī)器資源,這應(yīng)該怎么去估算呢,反正程序員在家里怎么算都算不出來。
評估訪問量
- 問運營,如果是一個已經(jīng)上線的產(chǎn)品,肯定存在已有的用戶數(shù)和訪問數(shù)據(jù),就算存在偏差,也是可控的范圍。
- 問產(chǎn)品,確定一個什么樣形態(tài)的產(chǎn)品,例如是拼團(tuán),例如是秒殺,各種處理方式都不同
評估平均訪問量qps
一天86400秒,一般認(rèn)為請求大部分發(fā)生在白天,就按照40000計算,日平均訪問量=日總訪問量/40000
評估高峰qps
可以把之前每日的訪問曲線圖拉出來看看,峰值是根據(jù)業(yè)務(wù)不同而定的,例如,有些業(yè)務(wù)是白天早上10點的流量偏多,有些業(yè)務(wù)是晚上人家休閑類的流量偏多,總之,根據(jù)業(yè)務(wù)去估算出日均的峰值,類似于電商類的服務(wù),一般峰值是日均流量的5倍左右。還有例如一些大促活動可能會更高,這個都要跟運營人員提前溝通好的,還有一些活動例如,秒殺,這個就不是靠預(yù)估出來,秒殺是另一種的考慮情況,采取的應(yīng)對策略跟普通訂單是完全不同。
評估系統(tǒng),單機(jī)極限qps
在上線之前需要跟測試人員一起做壓力測試,針對每個服務(wù)每臺機(jī)器去做,一般來說,會把一個服務(wù)一臺機(jī)器壓到極限,在逐步的進(jìn)行優(yōu)化。
思考一個問題,假定單臺機(jī)器最大的qps是1000,我們峰值是5000,那需要用多少臺機(jī)器去抗?答案是大于等于6臺,最少的容錯不得少于1臺。
貌似一個非常簡單的微服務(wù)就差不多,不過貌似還是差了很多,數(shù)一下:
- 監(jiān)控系統(tǒng)哪去了:(基礎(chǔ)設(shè)施監(jiān)控,系統(tǒng)監(jiān)控,應(yīng)用監(jiān)控,業(yè)務(wù)監(jiān)控)
- 網(wǎng)關(guān)哪里去了
- 統(tǒng)一的異常處理哪里去了
- API文檔哪里去了
- 容器化哪里去了
- 服務(wù)編排哪里去了
- …