分布式服務(wù)化系統(tǒng)一致性的“最佳實(shí)干”

本文歡迎轉(zhuǎn)載,轉(zhuǎn)載請(qǐng)注明原文鏈接,并附作者個(gè)人信息李艷鵬助被。

1 背景

一致性是一個(gè)抽象的佃蚜、具有多重含義的計(jì)算機(jī)術(shù)語(yǔ)结澄,在不同應(yīng)用場(chǎng)景下幽污,有不同的定義和含義吁津。在傳統(tǒng)的IT時(shí)代凭舶,一致性通常指強(qiáng)一致性咽筋,強(qiáng)一致性通常體現(xiàn)在你中有我溶推、我中有你、渾然一體奸攻;而在互聯(lián)網(wǎng)時(shí)代蒜危,一致性的含義遠(yuǎn)遠(yuǎn)超出了它原有的含義,在我們討論互聯(lián)網(wǎng)時(shí)代的一致性之前睹耐,我們先了解一下互聯(lián)網(wǎng)時(shí)代的特點(diǎn)辐赞,互聯(lián)網(wǎng)時(shí)代信息量巨大、需要計(jì)算能力巨大硝训,不但對(duì)用戶響應(yīng)速度要求快响委,而且吞吐量指標(biāo)也要向外擴(kuò)展(既:水平伸縮),于是單節(jié)點(diǎn)的服務(wù)器無(wú)法滿足需求窖梁,服務(wù)節(jié)點(diǎn)開始池化赘风,想想那個(gè)經(jīng)典的故事,一只筷子一折就斷纵刘,一把筷子怎么都折不斷邀窃,可見人多力量大的思想是多么的重要,但是人多也不一定能解決所有事情彰导,還得進(jìn)行有序蛔翅、合理的分配任務(wù),進(jìn)行有效的管理位谋,于是互聯(lián)網(wǎng)時(shí)代談?wù)撟疃嗟脑掝}就是拆分山析,拆分一般分為“水平拆分”和“垂直拆分”(大家不要對(duì)應(yīng)到數(shù)據(jù)庫(kù)或者緩存拆分,這里主要表達(dá)一種邏輯)掏父。這里笋轨,“水平拆分”指的是同一個(gè)功能由于單機(jī)節(jié)點(diǎn)無(wú)法滿足性能需求,需要擴(kuò)展成為多節(jié)點(diǎn)赊淑,多個(gè)節(jié)點(diǎn)具有一致的功能爵政,組成一個(gè)服務(wù)池,一個(gè)節(jié)點(diǎn)服務(wù)一部分的請(qǐng)求量陶缺,團(tuán)結(jié)起來(lái)共同處理大規(guī)模高并發(fā)的請(qǐng)求量钾挟。“垂直拆分”指的是按照功能拆分饱岸,秉著“專業(yè)的人干專業(yè)的事兒”的原則掺出,把一個(gè)復(fù)雜的功能拆分到多個(gè)單一的簡(jiǎn)單的元功能徽千,不同的元功能組合在一起,和未拆分前完成的功能是一致的汤锨,由于每個(gè)元功能職責(zé)單一双抽、功能簡(jiǎn)單,讓維護(hù)和變更都變得更簡(jiǎn)單闲礼、安全牍汹,更易于產(chǎn)品版本的迭代,在這樣的一個(gè)互聯(lián)網(wǎng)的時(shí)代和環(huán)境柬泽,一致性指分布式服務(wù)化系統(tǒng)之間的弱一致性慎菲,包括應(yīng)用系統(tǒng)一致性和數(shù)據(jù)一致性。

無(wú)論是水平拆分還是垂直拆分锨并,都解決了特定場(chǎng)景下的特定問(wèn)題钧嘶,凡事有好的一面,都會(huì)有壞的一面琳疏,拆分后的系統(tǒng)或者服務(wù)化的系統(tǒng)最大的問(wèn)題就是一致性問(wèn)題,這么多個(gè)具有元功能的模塊闸拿,或者同一個(gè)功能池中的多個(gè)節(jié)點(diǎn)之間空盼,如何保證他們的信息是一致的、工作步伐是一致的新荤、狀態(tài)是一致的揽趾、互相協(xié)調(diào)有序的工作呢?

本文根據(jù)作者在互聯(lián)網(wǎng)企業(yè)的實(shí)際項(xiàng)目經(jīng)驗(yàn)苛骨,對(duì)服務(wù)化系統(tǒng)中最難解決的一致性問(wèn)題進(jìn)行研究和探討篱瞎,試圖從實(shí)踐經(jīng)驗(yàn)中找到規(guī)律,抽象出模式痒芝,分享給大家俐筋,希望對(duì)大家的項(xiàng)目實(shí)施有所幫助,在對(duì)實(shí)踐的總結(jié)中也會(huì)對(duì)相關(guān)的一致性術(shù)語(yǔ)做最樸實(shí)的解釋严衬,希望能幫助大家徹底理解一致性的本質(zhì)澄者,并能將其應(yīng)用到實(shí)踐,解決讀者現(xiàn)實(shí)中遇到的服務(wù)化系統(tǒng)的一致性問(wèn)題请琳,本文使用理論與實(shí)踐相結(jié)合的方法粱挡,突出在實(shí)踐中解決問(wèn)題的模式,因此叫做《分布式服務(wù)化系統(tǒng)一致性的“最佳實(shí)干”》俄精。

2 問(wèn)題

本節(jié)列舉不一致會(huì)導(dǎo)致的種種問(wèn)題询筏,這也包括一例生活中的問(wèn)題。

案例1:買房

假如你想要享受生活的隨意竖慧,只想買個(gè)兩居嫌套,不想讓房貸有太大壓力逆屡,而你媳婦卻想要買個(gè)三居,還得帶花園的灌危,那么你們就不一致了康二,不一致導(dǎo)致生活不愉快、不協(xié)調(diào)勇蝙,嚴(yán)重情況下還會(huì)吵架沫勿,可見生活中的不一致問(wèn)題影響很大。

案例2:轉(zhuǎn)賬

轉(zhuǎn)賬是經(jīng)典的不一致案例味混,設(shè)想一下銀行為你處理一筆轉(zhuǎn)賬产雹,扣減你賬戶上的余額,然后增加別人賬戶的余額翁锡;如果扣減你的賬戶余額成功蔓挖,增加別人賬戶余額失敗,那么你就會(huì)損失這筆資金馆衔。反過(guò)來(lái)瘟判,如果扣減你的賬戶余額失敗,增加別人賬戶余額成功角溃,那么銀行就會(huì)損失這筆資金拷获,銀行需要賠付。對(duì)于資金處理系統(tǒng)來(lái)說(shuō)减细,上面任何一種場(chǎng)景都是不允許發(fā)生的匆瓜,一旦發(fā)生就會(huì)有資金損失,后果是不堪設(shè)想的未蝌,嚴(yán)重情況會(huì)讓一個(gè)公司瞬間倒閉驮吱,可參考案例

案例3:下訂單和扣庫(kù)存

電商系統(tǒng)中也有一個(gè)經(jīng)典的案例萧吠,下訂單和扣庫(kù)存如何保持一致左冬,如果先下訂單,扣庫(kù)存失敗怎憋,那么將會(huì)導(dǎo)致超賣又碌;如果下訂單沒有成功,扣庫(kù)存成功绊袋,那么會(huì)導(dǎo)致少賣毕匀。兩種情況都會(huì)導(dǎo)致運(yùn)營(yíng)成本的增加,嚴(yán)重情況下需要賠付癌别。

案例4:同步超時(shí)

服務(wù)化的系統(tǒng)間調(diào)用常常因?yàn)榫W(wǎng)絡(luò)問(wèn)題導(dǎo)致系統(tǒng)間調(diào)用超時(shí)皂岔,即使是網(wǎng)絡(luò)很好的機(jī)房,在億次流量的基數(shù)下展姐,同步調(diào)用超時(shí)也是家常便飯躁垛。系統(tǒng)A同步調(diào)用系統(tǒng)B超時(shí)剖毯,系統(tǒng)A可以明確得到超時(shí)反饋,但是無(wú)法確定系統(tǒng)B是否已經(jīng)完成了預(yù)定的功能或者沒有完成預(yù)定的功能教馆。于是逊谋,系統(tǒng)A就迷茫了,不知道應(yīng)該繼續(xù)做什么土铺,如何反饋給使用方胶滋。(曾經(jīng)的一個(gè)B2B產(chǎn)品的客戶要求接口超時(shí)重新通知他們,這個(gè)在技術(shù)上是難以實(shí)現(xiàn)的悲敷,因?yàn)榉?wù)器本身可能并不知道自己超時(shí)究恤,可能會(huì)繼續(xù)正常的返回?cái)?shù)據(jù),只是客戶端并沒有接受到結(jié)果罷了后德,因此這不是一個(gè)合理的解決方案)部宿。

案例5:異步回調(diào)超時(shí)

此案例和上一個(gè)同步超時(shí)案例類似,不過(guò)這個(gè)場(chǎng)景使用了異步回調(diào)瓢湃,系統(tǒng)A同步調(diào)用系統(tǒng)B發(fā)起指令理张,系統(tǒng)B采用受理模式,受理后則返回受理成功绵患,然后系統(tǒng)B異步通知系統(tǒng)A涯穷。在這個(gè)過(guò)程中,如果系統(tǒng)A由于某種原因遲遲沒有收到回調(diào)結(jié)果藏雏,那么兩個(gè)系統(tǒng)間的狀態(tài)就不一致,互相認(rèn)知不同會(huì)導(dǎo)致系統(tǒng)間發(fā)生錯(cuò)誤作煌,嚴(yán)重情況下會(huì)影響核心事務(wù)掘殴,甚至?xí)?dǎo)致資金損失。

案例6:掉單

分布式系統(tǒng)中粟誓,兩個(gè)系統(tǒng)協(xié)作處理一個(gè)流程奏寨,分別為對(duì)方的上下游,如果一個(gè)系統(tǒng)中存在一個(gè)請(qǐng)求鹰服,通常指訂單病瞳,另外一個(gè)系統(tǒng)不存在,則導(dǎo)致掉單悲酷,掉單的后果很嚴(yán)重套菜,有時(shí)候也會(huì)導(dǎo)致資金損失。

案例7:系統(tǒng)間狀態(tài)不一致

這個(gè)案例與上面掉單案例類似设易,不同的是兩個(gè)系統(tǒng)間都存在請(qǐng)求逗柴,但是請(qǐng)求的狀態(tài)不一致。

案例8:緩存和數(shù)據(jù)庫(kù)不一致

交易相關(guān)系統(tǒng)基本離不開關(guān)系型數(shù)據(jù)庫(kù)顿肺,依賴關(guān)系型數(shù)據(jù)庫(kù)提供的ACID特性(后面介紹)戏溺,但是在大規(guī)模高并發(fā)的互聯(lián)網(wǎng)系統(tǒng)里渣蜗,一些特殊的場(chǎng)景對(duì)讀的性能要求極高,服務(wù)于交易的數(shù)據(jù)庫(kù)難以抗住大規(guī)模的讀流量旷祸,通常需要在數(shù)據(jù)庫(kù)前墊緩存耕拷,那么緩存和數(shù)據(jù)庫(kù)之間的數(shù)據(jù)如何保持一致性?是要保持強(qiáng)一致呢還是弱一致性呢托享?

案例9:本地緩存節(jié)點(diǎn)間不一致

一個(gè)服務(wù)池上的多個(gè)節(jié)點(diǎn)為了滿足較高的性能需求骚烧,需要使用本地緩存,使用了本地緩存嫌吠,每個(gè)節(jié)點(diǎn)都會(huì)有一份緩存數(shù)據(jù)的拷貝止潘,如果這些數(shù)據(jù)是靜態(tài)的、不變的辫诅,那永遠(yuǎn)都不會(huì)有問(wèn)題凭戴,但是如果這些數(shù)據(jù)是半靜態(tài)的或者常被更新的,當(dāng)被更新的時(shí)候炕矮,各個(gè)節(jié)點(diǎn)更新是有先后順序的么夫,在更新的瞬間,各個(gè)節(jié)點(diǎn)的數(shù)據(jù)是不一致的肤视,如果這些數(shù)據(jù)是為某一個(gè)開關(guān)服務(wù)的档痪,想象一下重復(fù)的請(qǐng)求走進(jìn)了不同的節(jié)點(diǎn)(在failover或者補(bǔ)償導(dǎo)致的場(chǎng)景下,重復(fù)請(qǐng)求是一定會(huì)發(fā)生的,也是服務(wù)化系統(tǒng)必須處理的)蔚袍,一個(gè)請(qǐng)求走了開關(guān)打開的邏輯翔怎,同時(shí)另外一個(gè)請(qǐng)求走了開關(guān)關(guān)閉的邏輯,這導(dǎo)致請(qǐng)求被處理兩次乐纸,最壞的情況下會(huì)導(dǎo)致災(zāi)難性的后果,就是資金損失摇予。

案例10:緩存數(shù)據(jù)結(jié)構(gòu)不一致

這個(gè)案例會(huì)時(shí)有發(fā)生汽绢,某系統(tǒng)需要種某一數(shù)據(jù)結(jié)構(gòu)的緩存,這一數(shù)據(jù)結(jié)構(gòu)有多個(gè)數(shù)據(jù)元素組成侧戴,其中宁昭,某個(gè)數(shù)據(jù)元素都需要從數(shù)據(jù)庫(kù)中或者服務(wù)中獲取,如果一部分?jǐn)?shù)據(jù)元素獲取失敗酗宋,由于程序處理不正確积仗,仍然將不完全的數(shù)據(jù)結(jié)構(gòu)存入緩存,那么緩存的消費(fèi)者消費(fèi)的時(shí)候很有可能因?yàn)闆]有合理處理異常情況而出錯(cuò)蜕猫。

3 模式

3.1 生活中不一致問(wèn)題的解決

大家回顧一下上一節(jié)列舉的生活中的案例1-買房斥扛,如果置身事外來(lái)看,解決這種不一致的辦法有兩個(gè),一個(gè)是避免不一致的發(fā)生稀颁,如果已經(jīng)是媳婦了就不好辦了:)芬失,還有一種方法就是慢慢的補(bǔ)償,先買個(gè)兩居匾灶,然后慢慢的等資金充裕了再換三居棱烂,買比特幣賺了再換帶花園的房子,于是問(wèn)題最終被解決了阶女,最終大家處于一致的狀態(tài)颊糜,都開心了。這樣可以解決案例1的問(wèn)題秃踩,很自然由于有了過(guò)渡的方法衬鱼,問(wèn)題在不經(jīng)意間就消失了,可見“過(guò)渡”也是解決一致性問(wèn)題的一個(gè)模式憔杨。

從案例1的解決方案來(lái)看鸟赫,我們要解決一致性問(wèn)題,一個(gè)最直接最簡(jiǎn)單的方法就是保持強(qiáng)一致性消别,對(duì)于案例1的情況抛蚤,盡量避免在結(jié)婚前兩個(gè)人能夠互相了解達(dá)成一致,避免不一致問(wèn)題的發(fā)生寻狂;不過(guò)有些事情事已至此岁经,發(fā)生了就是發(fā)生了,出現(xiàn)了不一致的問(wèn)題蛇券,我們應(yīng)該考慮去補(bǔ)償缀壤,盡最大的努力從不一致狀態(tài)修復(fù)到一致狀態(tài),避免損失全部或者一部分纠亚,也不失為一個(gè)好方法诉位。

因此,避免不一致是上策菜枷,出現(xiàn)了不一致及時(shí)發(fā)現(xiàn)及時(shí)修復(fù)是中策,有問(wèn)題不積極解決留給他人解決是下策叁丧。

3.2 酸堿平衡理論

ACID在英文中的意思是“酸”啤誊,BASE的意識(shí)是“堿”,這一段講的是“酸堿平衡”的故事拥娄。

1. ACID(酸)

如何保證強(qiáng)一致性呢蚊锹?計(jì)算機(jī)專業(yè)的童鞋在學(xué)習(xí)關(guān)系型數(shù)據(jù)庫(kù)的時(shí)候都學(xué)習(xí)了ACID原理,這里對(duì)ACID做個(gè)簡(jiǎn)單的介紹稚瘾。如果想全面的學(xué)習(xí)ACID原理牡昆,請(qǐng)參考ACID

關(guān)系型數(shù)據(jù)庫(kù)天生就是解決具有復(fù)雜事務(wù)場(chǎng)景的問(wèn)題,關(guān)系型數(shù)據(jù)庫(kù)完全滿足ACID的特性丢烘。

ACID指的是:

  • A: Atomicity柱宦,原子性
  • C: Consistency,一致性
  • I: Isolation播瞳,隔離性
  • D: Durability掸刊,持久性

具有ACID的特性的數(shù)據(jù)庫(kù)支持強(qiáng)一致性,強(qiáng)一致性代表數(shù)據(jù)庫(kù)本身不會(huì)出現(xiàn)不一致赢乓,每個(gè)事務(wù)是原子的忧侧,或者成功或者失敗,事物間是隔離的牌芋,互相完全不影響蚓炬,而且最終狀態(tài)是持久落盤的,因此躺屁,數(shù)據(jù)庫(kù)會(huì)從一個(gè)明確的狀態(tài)到另外一個(gè)明確的狀態(tài)肯夏,中間的臨時(shí)狀態(tài)是不會(huì)出現(xiàn)的,如果出現(xiàn)也會(huì)及時(shí)的自動(dòng)的修復(fù)楼咳,因此是強(qiáng)一致的熄捍。

3個(gè)典型的關(guān)系型數(shù)據(jù)庫(kù)Oracle、Mysql母怜、Db2都能保證強(qiáng)一致性余耽,Oracle和Mysql使用多版本控制協(xié)議實(shí)現(xiàn),而DB2使用改進(jìn)的兩階段提交協(xié)議來(lái)實(shí)現(xiàn)苹熏。

如果你在為交易相關(guān)系統(tǒng)做技術(shù)選型碟贾,交易的存儲(chǔ)應(yīng)該只考慮關(guān)系型數(shù)據(jù)庫(kù),對(duì)于核心系統(tǒng)轨域,如果需要較好的性能袱耽,可以考慮使用更強(qiáng)悍的硬件,這種向上擴(kuò)展(升級(jí)硬件)雖然成本較高干发,但是是最簡(jiǎn)單粗暴有效的方式朱巨,另外,Nosql完全不適合交易場(chǎng)景枉长,Nosql主要用來(lái)做數(shù)據(jù)分析冀续、ETL、報(bào)表必峰、數(shù)據(jù)挖掘洪唐、推薦、日志處理等非交易場(chǎng)景吼蚁。

前面提到的案例2-轉(zhuǎn)賬案例3-下訂單和扣庫(kù)存都可以利用關(guān)系型數(shù)據(jù)庫(kù)的強(qiáng)一致性解決凭需。

然而,前面提到,互聯(lián)網(wǎng)項(xiàng)目多數(shù)具有大規(guī)模高并發(fā)的特性粒蜈,必須應(yīng)用拆分的理念顺献,對(duì)高并發(fā)的壓力采取“大而化小、小而化了”的方法薪伏,否則難以滿足動(dòng)輒億級(jí)流量的需求滚澜,即使使用關(guān)系型數(shù)據(jù)庫(kù),單機(jī)也難以滿足存儲(chǔ)和TPS上的需求嫁怀。為了保證案例2-轉(zhuǎn)賬可以利用關(guān)系型數(shù)據(jù)庫(kù)的強(qiáng)一致性设捐,在拆分的時(shí)候盡量的把轉(zhuǎn)賬相關(guān)的賬戶放入一個(gè)數(shù)據(jù)庫(kù)分片,對(duì)于案例3塘淑,盡量的保證把訂單和庫(kù)存放入同一個(gè)數(shù)據(jù)庫(kù)分片萝招,這樣通過(guò)關(guān)系型數(shù)據(jù)庫(kù)自然就解決了不一致的問(wèn)題。

然而存捺,有些時(shí)候事與愿違槐沼,由于業(yè)務(wù)規(guī)則的限制,無(wú)法將相關(guān)的數(shù)據(jù)分到同一個(gè)數(shù)據(jù)庫(kù)分片捌治,這個(gè)時(shí)候我們就需要實(shí)現(xiàn)最終一致性岗钩。

對(duì)于案例2-轉(zhuǎn)賬場(chǎng)景,假設(shè)賬戶數(shù)量巨大肖油,對(duì)賬戶存儲(chǔ)進(jìn)行了拆分兼吓,關(guān)系型數(shù)據(jù)庫(kù)一共分了8個(gè)實(shí)例,每個(gè)實(shí)例8個(gè)庫(kù)森枪,每個(gè)庫(kù)8個(gè)表视搏,共512張表,假如要轉(zhuǎn)賬的兩個(gè)賬戶正好落在了一個(gè)庫(kù)里县袱,那么可以依賴關(guān)系型數(shù)據(jù)庫(kù)的事務(wù)保持強(qiáng)一致性浑娜。

如果要轉(zhuǎn)賬的兩個(gè)賬戶正好落在了不同的庫(kù)里,轉(zhuǎn)賬操作是無(wú)法封裝在同一個(gè)數(shù)據(jù)庫(kù)事務(wù)中的式散,這個(gè)時(shí)候會(huì)發(fā)生一個(gè)庫(kù)的賬戶扣減余額成功筋遭,另外一個(gè)庫(kù)的賬戶增加余額失敗的情況。

對(duì)于這種情況暴拄,我們需要繼續(xù)探討解決之道漓滔,CAP原理和BASE原理,BASE原理通過(guò)記錄事務(wù)的中間的臨時(shí)狀態(tài)揍移,實(shí)現(xiàn)最終一致性。

2. CAP(帽子理論)

如果想深入的學(xué)習(xí)CAP理論反肋,請(qǐng)參考CAP那伐。

由于對(duì)系統(tǒng)或者數(shù)據(jù)進(jìn)行了拆分,我們的系統(tǒng)不再是單機(jī)系統(tǒng),而是分布式系統(tǒng)罕邀,針對(duì)分布式系的帽子理論包含三個(gè)元素:

  • C:Consistency畅形,一致性, 數(shù)據(jù)一致更新,所有數(shù)據(jù)變動(dòng)都是同步的
  • A:Availability诉探,可用性, 好的響應(yīng)性能日熬,完全的可用性指的是在任何故障模型下,服務(wù)都會(huì)在有限的時(shí)間處理響應(yīng)
  • P:Partition tolerance肾胯,分區(qū)容錯(cuò)性竖席,可靠性

帽子理論證明,任何分布式系統(tǒng)只可同時(shí)滿足二點(diǎn)敬肚,沒法三者兼顧毕荐。關(guān)系型數(shù)據(jù)庫(kù)由于關(guān)系型數(shù)據(jù)庫(kù)是單節(jié)點(diǎn)的,因此艳馒,不具有分區(qū)容錯(cuò)性憎亚,但是具有一致性和可用性,而分布式的服務(wù)化系統(tǒng)都需要滿足分區(qū)容錯(cuò)性弄慰,那么我們必須在一致性和可用性中進(jìn)行權(quán)衡第美,具體表現(xiàn)在服務(wù)化系統(tǒng)處理的異常請(qǐng)求在某一個(gè)時(shí)間段內(nèi)可能是不完全的,但是經(jīng)過(guò)自動(dòng)的或者手工的補(bǔ)償后陆爽,達(dá)到了最終的一致性什往。

3. BASE(堿)

BASE理論解決CAP理論提出了分布式系統(tǒng)的一致性和可用性不能兼得的問(wèn)題,如果想全面的學(xué)習(xí)BASE原理墓陈,請(qǐng)參考Eventual consistency恶守。

BASE在英文中有“堿”的意思,對(duì)應(yīng)本節(jié)開頭的ACID在英文中“酸”的意思贡必,基于這兩個(gè)名詞提出了酸堿平衡的結(jié)論兔港,簡(jiǎn)單來(lái)說(shuō)是在不同的場(chǎng)景下,可以分別利用ACID和BASE來(lái)解決分布式服務(wù)化系統(tǒng)的一致性問(wèn)題仔拟。

BASE模型與ACID模型截然不同衫樊,滿足CAP理論,通過(guò)犧牲強(qiáng)一致性利花,獲得可用性科侈,一般應(yīng)用在服務(wù)化系統(tǒng)的應(yīng)用層或者大數(shù)據(jù)處理系統(tǒng),通過(guò)達(dá)到最終一致性來(lái)盡量滿足業(yè)務(wù)的絕大部分需求炒事。

BASE模型包含個(gè)三個(gè)元素:

  • BA:Basically Available臀栈,基本可用
  • S:Soft State,軟狀態(tài)挠乳,狀態(tài)可以有一段時(shí)間不同步
  • E:Eventually Consistent权薯,最終一致姑躲,最終數(shù)據(jù)是一致的就可以了,而不是時(shí)時(shí)保持強(qiáng)一致

BASE模型的軟狀態(tài)是實(shí)現(xiàn)BASE理論的方法盟蚣,基本可用和最終一致是目標(biāo)黍析。按照BASE模型實(shí)現(xiàn)的系統(tǒng),由于不保證強(qiáng)一致性屎开,系統(tǒng)在處理請(qǐng)求的過(guò)程中阐枣,可以存在短暫的不一致,在短暫的不一致窗口請(qǐng)求處理處在臨時(shí)狀態(tài)中奄抽,系統(tǒng)在做每步操作的時(shí)候蔼两,通過(guò)記錄每一個(gè)臨時(shí)狀態(tài),在系統(tǒng)出現(xiàn)故障的時(shí)候如孝,可以從這些中間狀態(tài)繼續(xù)未完成的請(qǐng)求處理或者退回到原始狀態(tài)宪哩,最后達(dá)到一致的狀態(tài)。

案例1-轉(zhuǎn)賬為例第晰,我們把用戶A給用戶B轉(zhuǎn)賬分成四個(gè)階段锁孟,第一個(gè)階段用戶A準(zhǔn)備轉(zhuǎn)賬,第二個(gè)階段從用戶A賬戶扣減余額茁瘦,第三個(gè)階段對(duì)用戶B增加余額品抽,第四個(gè)階段完成轉(zhuǎn)賬。系統(tǒng)需要記錄操作過(guò)程中每一步驟的狀態(tài)甜熔,一旦系統(tǒng)出現(xiàn)故障圆恤,系統(tǒng)能夠自動(dòng)發(fā)現(xiàn)沒有完成的任務(wù),然后腔稀,根據(jù)任務(wù)所處的狀態(tài)盆昙,繼續(xù)執(zhí)行任務(wù),最終完成任務(wù)焊虏,達(dá)到一致的最終狀態(tài)淡喜。

在實(shí)際應(yīng)用中,上面這個(gè)過(guò)程通常是通過(guò)持久化執(zhí)行任務(wù)的狀態(tài)和環(huán)境信息诵闭,一旦出現(xiàn)問(wèn)題炼团,定時(shí)任務(wù)會(huì)撈取未執(zhí)行完的任務(wù),繼續(xù)未執(zhí)行完的任務(wù)疏尿,直到執(zhí)行完成為止瘟芝,或者取消已經(jīng)完成的部分操作回到原始狀態(tài)。這種方法在任務(wù)完成每個(gè)階段的時(shí)候褥琐,都要更新數(shù)據(jù)庫(kù)中任務(wù)的狀態(tài)锌俱,這在大規(guī)模高并發(fā)系統(tǒng)中不會(huì)有太好的性能,一個(gè)更好的辦法是用Write-Ahead Log(寫前日志)敌呈,這和數(shù)據(jù)庫(kù)的Bin Log(操作日志)相似,在做每一個(gè)操作步驟,都先寫入日志零抬,如果操作遇到問(wèn)題而停止的時(shí)候凿试,可以讀取日志按照步驟進(jìn)行恢復(fù),并且繼續(xù)執(zhí)行未完成的工作褐鸥,最后達(dá)到一致线脚。寫前日志可以利用機(jī)械硬盤的追加寫而達(dá)到較好性能,因此叫榕,這是一種專業(yè)化的實(shí)現(xiàn)方式浑侥,多數(shù)業(yè)務(wù)系系統(tǒng)還是使用數(shù)據(jù)庫(kù)記錄的字段來(lái)記錄任務(wù)的執(zhí)行狀態(tài),也就是記錄中間的“軟狀態(tài)”晰绎,一個(gè)任務(wù)的狀態(tài)流轉(zhuǎn)一般可以通過(guò)數(shù)據(jù)庫(kù)的行級(jí)鎖來(lái)實(shí)現(xiàn)寓落,這比使用Write-Ahead Log實(shí)現(xiàn)更簡(jiǎn)單、更快速荞下。

有了BASE理論作為基礎(chǔ)伶选,我們對(duì)復(fù)雜的分布式事務(wù)進(jìn)行拆解,對(duì)其中的每一步驟都記錄其狀態(tài)尖昏,有問(wèn)題的時(shí)候可以根據(jù)記錄的狀態(tài)來(lái)繼續(xù)執(zhí)行任務(wù)仰税,達(dá)到最終的一致,通過(guò)這個(gè)方法我們可以解決案例2-轉(zhuǎn)賬案例3-下訂單和扣庫(kù)存中遇到的問(wèn)題抽诉。

4. 酸堿平衡的總結(jié)

  1. 使用向上擴(kuò)展(強(qiáng)悍的硬件)運(yùn)行專業(yè)的關(guān)系型數(shù)據(jù)庫(kù)(例如:Oracle或者DB2)能夠保證強(qiáng)一致性陨簇,錢能解決的問(wèn)題就不是問(wèn)題
  1. 如果錢是問(wèn)題,可以對(duì)廉價(jià)硬件運(yùn)行的開源關(guān)系型數(shù)據(jù)庫(kù)(例如:Mysql)進(jìn)行分片迹淌,將相關(guān)的數(shù)據(jù)分到數(shù)據(jù)庫(kù)的同一個(gè)片河绽,仍然能夠使用關(guān)系型數(shù)據(jù)庫(kù)保證事務(wù)
  2. 如果業(yè)務(wù)規(guī)則限制,無(wú)法將相關(guān)的數(shù)據(jù)分到同一個(gè)片唉窃,就需要實(shí)現(xiàn)最終一致性耙饰,通過(guò)記錄事務(wù)的軟狀態(tài)(中間狀態(tài)、臨時(shí)狀態(tài))句携,一旦處于不一致榔幸,可以通過(guò)系統(tǒng)自動(dòng)化或者人工干預(yù)來(lái)修復(fù)不一致的情況

3.3 分布式一致性協(xié)議

國(guó)際開放標(biāo)準(zhǔn)組織Open Group定義了DTS(分布式事務(wù)處理模型),模型中包含4個(gè)角色:應(yīng)用程序矮嫉、事務(wù)管理器削咆、資源管理器、通信資源管理器四部分蠢笋。事務(wù)處理器是統(tǒng)管全局的管理者拨齐,資源處理器和通信資源處理器是事務(wù)的參與者。

J2EE規(guī)范也包含此分布式事務(wù)處理模型的規(guī)范昨寞,并在所有的AppServer中進(jìn)行實(shí)現(xiàn)瞻惋,J2EE規(guī)范中定義了TX協(xié)議和XA協(xié)議厦滤,TX協(xié)議定義應(yīng)用程序與事務(wù)管理器之間的接口,而XA協(xié)議定義了事務(wù)管理器與資源處理器之間的接口歼狼,在過(guò)去掏导,大家使用AppServer,例如:Websphere羽峰、Weblogic趟咆、Jboss等配置數(shù)據(jù)源的時(shí)候會(huì)看見類似XADatasource的數(shù)據(jù)源,這就是實(shí)現(xiàn)了DTS的關(guān)系型數(shù)據(jù)庫(kù)的數(shù)據(jù)源梅屉。企業(yè)級(jí)開發(fā)JEE中值纱,關(guān)系型數(shù)據(jù)庫(kù)、JMS服務(wù)扮演資源管理器的角色坯汤,而EJB容器則扮演事務(wù)管理器的角色虐唠。

下面我們就介紹兩階段提交協(xié)議三階段提交協(xié)議以及阿里巴巴提出的TCC惰聂,它們都是根據(jù)DTS這一思想演變出來(lái)的疆偿。

1. 兩階段提交協(xié)議

上面描述的JEE的XA協(xié)議就是根據(jù)兩階段提交來(lái)保證事務(wù)的完整性,并實(shí)現(xiàn)分布式服務(wù)化的強(qiáng)一致性搓幌。

兩階段提交協(xié)議把分布式事務(wù)分成兩個(gè)過(guò)程翁脆,一個(gè)是準(zhǔn)備階段,一個(gè)是提交階段鼻种,準(zhǔn)備階段和提交階段都是由事務(wù)管理器發(fā)起的反番,為了接下來(lái)講解方便,我們把事務(wù)管理器稱為協(xié)調(diào)者叉钥,把資管管理器稱為參與者罢缸。

兩階段如下:

  1. 準(zhǔn)備階段:協(xié)調(diào)者向參與者發(fā)起指令,參與者評(píng)估自己的狀態(tài)投队,如果參與者評(píng)估指令可以完成枫疆,參與者會(huì)寫redo或者undo日志(這也是前面提起的Write-Ahead Log的一種),然后鎖定資源敷鸦,執(zhí)行操作息楔,但是并不提交
  1. 提交階段:如果每個(gè)參與者明確返回準(zhǔn)備成功,也就是預(yù)留資源和執(zhí)行操作成功扒披,協(xié)調(diào)者向參與者發(fā)起提交指令值依,參與者提交資源變更的事務(wù),釋放鎖定的資源碟案;如果任何一個(gè)參與者明確返回準(zhǔn)備失敗愿险,也就是預(yù)留資源或者執(zhí)行操作失敗,協(xié)調(diào)者向參與者發(fā)起中止指令价说,參與者取消已經(jīng)變更的事務(wù)辆亏,執(zhí)行undo日志风秤,釋放鎖定的資源

兩階段提交協(xié)議成功場(chǎng)景示意圖如下:

兩階段提交協(xié)議

我們看到兩階段提交協(xié)議在準(zhǔn)備階段鎖定資源,是一個(gè)重量級(jí)的操作扮叨,并能保證強(qiáng)一致性缤弦,但是實(shí)現(xiàn)起來(lái)復(fù)雜、成本較高彻磁,不夠靈活甸鸟,更重要的是它有如下致命的問(wèn)題:

  1. 阻塞:從上面的描述來(lái)看,對(duì)于任何一次指令必須收到明確的響應(yīng)兵迅,才會(huì)繼續(xù)做下一步,否則處于阻塞狀態(tài)薪贫,占用的資源被一直鎖定恍箭,不會(huì)被釋放
  1. 單點(diǎn)故障:如果協(xié)調(diào)者宕機(jī),參與者沒有了協(xié)調(diào)者指揮瞧省,會(huì)一直阻塞扯夭,盡管可以通過(guò)選舉新的協(xié)調(diào)者替代原有協(xié)調(diào)者,但是如果之前協(xié)調(diào)者在發(fā)送一個(gè)提交指令后宕機(jī)鞍匾,而提交指令僅僅被一個(gè)參與者接受交洗,并且參與者接收后也宕機(jī),新上任的協(xié)調(diào)者無(wú)法處理這種情況
  2. 腦裂:協(xié)調(diào)者發(fā)送提交指令橡淑,有的參與者接收到執(zhí)行了事務(wù)构拳,有的參與者沒有接收到事務(wù),就沒有執(zhí)行事務(wù)梁棠,多個(gè)參與者之間是不一致的

上面所有的這些問(wèn)題置森,都是需要人工干預(yù)處理,沒有自動(dòng)化的解決方案符糊,因此兩階段提交協(xié)議在正常情況下能保證系統(tǒng)的強(qiáng)一致性凫海,但是在出現(xiàn)異常情況下,當(dāng)前處理的操作處于錯(cuò)誤狀態(tài)男娄,需要管理員人工干預(yù)解決行贪,因此可用性不夠好,這也符合CAP協(xié)議的一致性和可用性不能兼得的原理模闲。

2. 三階段提交協(xié)議

三階段提交協(xié)議是兩階段提交協(xié)議的改進(jìn)版本建瘫。它通過(guò)超時(shí)機(jī)制解決了阻塞的問(wèn)題,并且把兩個(gè)階段增加為三個(gè)階段:

  1. 詢問(wèn)階段:協(xié)調(diào)者詢問(wèn)參與者是否可以完成指令尸折,協(xié)調(diào)者只需要回答是還是不是暖混,而不需要做真正的操作,這個(gè)階段超時(shí)導(dǎo)致中止
  1. 準(zhǔn)備階段:如果在詢問(wèn)階段所有的參與者都返回可以執(zhí)行操作翁授,協(xié)調(diào)者向參與者發(fā)送預(yù)執(zhí)行請(qǐng)求拣播,然后參與者寫redo和undo日志晾咪,執(zhí)行操作,但是不提交操作贮配;如果在詢問(wèn)階段任何參與者返回不能執(zhí)行操作的結(jié)果谍倦,則協(xié)調(diào)者向參與者發(fā)送中止請(qǐng)求,這里的邏輯與兩階段提交協(xié)議的的準(zhǔn)備階段是相似的泪勒,這個(gè)階段超時(shí)導(dǎo)致成功
  2. 提交階段:如果每個(gè)參與者在準(zhǔn)備階段返回準(zhǔn)備成功昼蛀,也就是預(yù)留資源和執(zhí)行操作成功,協(xié)調(diào)者向參與者發(fā)起提交指令圆存,參與者提交資源變更的事務(wù)叼旋,釋放鎖定的資源;如果任何一個(gè)參與者返回準(zhǔn)備失敗沦辙,也就是預(yù)留資源或者執(zhí)行操作失敗夫植,協(xié)調(diào)者向參與者發(fā)起中止指令,參與者取消已經(jīng)變更的事務(wù)油讯,執(zhí)行undo日志详民,釋放鎖定的資源,這里的邏輯與兩階段提交協(xié)議的提交階段一致

三階段提交協(xié)議成功場(chǎng)景示意圖如下:

三階段提交協(xié)議

然而陌兑,這里與兩階段提交協(xié)議有兩個(gè)主要的不同:

  1. 增加了一個(gè)詢問(wèn)階段沈跨,詢問(wèn)階段可以確保盡可能早的發(fā)現(xiàn)無(wú)法執(zhí)行操作而需要中止的行為,但是它并不能發(fā)現(xiàn)所有的這種行為兔综,只會(huì)減少這種情況的發(fā)生
  1. 在準(zhǔn)備階段以后饿凛,協(xié)調(diào)者和參與者執(zhí)行的任務(wù)中都增加了超時(shí),一旦超時(shí)软驰,協(xié)調(diào)者和參與者都繼續(xù)提交事務(wù)笤喳,默認(rèn)為成功,這也是根據(jù)概率統(tǒng)計(jì)上超時(shí)后默認(rèn)成功的正確性最大

三階段提交協(xié)議與兩階段提交協(xié)議相比碌宴,具有如上的優(yōu)點(diǎn)杀狡,但是一旦發(fā)生超時(shí),系統(tǒng)仍然會(huì)發(fā)生不一致贰镣,只不過(guò)這種情況很少見罷了呜象,好處就是至少不會(huì)阻塞和永遠(yuǎn)鎖定資源。

3. TCC

上面兩節(jié)講解了兩階段提交協(xié)議和三階段提交協(xié)議碑隆,實(shí)際上他們能解決案例2-轉(zhuǎn)賬案例3-下訂單和扣庫(kù)存中的分布式事務(wù)的問(wèn)題恭陡,但是遇到極端情況,系統(tǒng)會(huì)發(fā)生阻塞或者不一致的問(wèn)題上煤,需要運(yùn)營(yíng)或者技術(shù)人工解決休玩。無(wú)論兩階段還是三階段方案中都包含多個(gè)參與者、多個(gè)階段實(shí)現(xiàn)一個(gè)事務(wù),實(shí)現(xiàn)復(fù)雜拴疤,性能也是一個(gè)很大的問(wèn)題永部,因此,在互聯(lián)網(wǎng)高并發(fā)系統(tǒng)中呐矾,鮮有使用兩階段提交和三階段提交協(xié)議的場(chǎng)景苔埋。

阿里巴巴提出了新的TCC協(xié)議,TCC協(xié)議將一個(gè)任務(wù)拆分成Try蜒犯、Confirm组橄、Cancel,正常的流程會(huì)先執(zhí)行Try罚随,如果執(zhí)行沒有問(wèn)題玉工,再執(zhí)行Confirm,如果執(zhí)行過(guò)程中出了問(wèn)題淘菩,則執(zhí)行操作的逆操Cancel遵班,從正常的流程上講,這仍然是一個(gè)兩階段的提交協(xié)議瞄勾,但是,在執(zhí)行出現(xiàn)問(wèn)題的時(shí)候弥激,有一定的自我修復(fù)能力进陡,如果任何一個(gè)參與者出現(xiàn)了問(wèn)題,協(xié)調(diào)者通過(guò)執(zhí)行操作的逆操作來(lái)取消之前的操作微服,達(dá)到最終的一致狀態(tài)趾疚。

可以看出,從時(shí)序上以蕴,如果遇到極端情況下TCC會(huì)有很多問(wèn)題的糙麦,例如,如果在Cancel的時(shí)候一些參與者收到指令丛肮,而一些參與者沒有收到指令赡磅,整個(gè)系統(tǒng)仍然是不一致的,這種復(fù)雜的情況宝与,系統(tǒng)首先會(huì)通過(guò)補(bǔ)償?shù)姆绞椒倮龋瑖L試自動(dòng)修復(fù)的,如果系統(tǒng)無(wú)法修復(fù)习劫,必須由人工參與解決咆瘟。

從TCC的邏輯上看,可以說(shuō)TCC是簡(jiǎn)化版的三階段提交協(xié)議诽里,解決了兩階段提交協(xié)議的阻塞問(wèn)題袒餐,但是沒有解決極端情況下會(huì)出現(xiàn)不一致和腦裂的問(wèn)題。然而,TCC通過(guò)自動(dòng)化補(bǔ)償手段灸眼,會(huì)把需要人工處理的不一致情況降到到最少卧檐,也是一種非常有用的解決方案,根據(jù)線人幢炸,阿里在內(nèi)部的一些中間件上實(shí)現(xiàn)了TCC模式泄隔。

我們給出一個(gè)使用TCC的實(shí)際案例,在秒殺的場(chǎng)景宛徊,用戶發(fā)起下單請(qǐng)求佛嬉,應(yīng)用層先查詢庫(kù)存,確認(rèn)商品庫(kù)存還有余量闸天,則鎖定庫(kù)存暖呕,此時(shí)訂單狀態(tài)為待支付,然后指引用戶去支付苞氮,由于某種原因用戶支付失敗湾揽,或者支付超時(shí),系統(tǒng)會(huì)自動(dòng)將鎖定的庫(kù)存解鎖供其他用戶秒殺笼吟。

TCC協(xié)議使用場(chǎng)景示意圖如下:

TCC

總結(jié)一下库物,兩階段提交協(xié)議、三階段提交協(xié)議贷帮、TCC協(xié)議都能保證分布式事務(wù)的一致性戚揭,他們保證的分布式系統(tǒng)的一致性從強(qiáng)到弱,TCC達(dá)到的目標(biāo)是最終一致性撵枢,其中任何一種方法都可以不同程度的解決案例2:轉(zhuǎn)賬民晒、案例3:下訂單和扣庫(kù)存的問(wèn)題,只是實(shí)現(xiàn)的一致性的級(jí)別不一樣而已锄禽,對(duì)于案例4:同步超時(shí)可以通過(guò)TCC的理念解決潜必,如果同步調(diào)用超時(shí),調(diào)用方可以使用fastfail策略沃但,返回調(diào)用方的使用方失敗的結(jié)果磁滚,同時(shí)調(diào)用服務(wù)的逆向cancel操作,保證服務(wù)的最終一致性宵晚。

3.4 保證最終一致性的模式

在大規(guī)模高并發(fā)服務(wù)化系統(tǒng)中恨旱,一個(gè)功能被拆分成多個(gè)具有單一功能的元功能祟蚀,一個(gè)流程會(huì)有多個(gè)系統(tǒng)的多個(gè)元功能組合實(shí)現(xiàn)愈魏,如果使用兩階段提交協(xié)議和三階段提交協(xié)議鸭栖,確實(shí)能解決系統(tǒng)間一致性問(wèn)題智哀,除了這兩個(gè)協(xié)議帶來(lái)的自身的問(wèn)題帐萎,這些協(xié)議的實(shí)現(xiàn)比較復(fù)雜娜庇、成本比較高蛮粮,最重要的是性能并不好出吹,相比來(lái)看,TCC協(xié)議更簡(jiǎn)單掂名、容易實(shí)現(xiàn)据沈,但是TCC協(xié)議由于每個(gè)事務(wù)都需要執(zhí)行Try,再執(zhí)行Confirm饺蔑,略微顯得臃腫锌介,因此,在現(xiàn)實(shí)的系統(tǒng)中猾警,底線要求僅僅需要能達(dá)到最終一致性孔祸,而不需要實(shí)現(xiàn)專業(yè)的、復(fù)雜的一致性協(xié)議发皿,實(shí)現(xiàn)最終一致性有一些非常有效的崔慧、簡(jiǎn)單粗暴的模式,下面就介紹這些模式及其應(yīng)用場(chǎng)景穴墅。

1. 查詢模式

任何一個(gè)服務(wù)操作都需要提供一個(gè)查詢接口惶室,用來(lái)向外部輸出操作執(zhí)行的狀態(tài)。服務(wù)操作的使用方可以通過(guò)查詢接口玄货,得知服務(wù)操作執(zhí)行的狀態(tài)皇钞,然后根據(jù)不同狀態(tài)來(lái)做不同的處理操作。

為了能夠?qū)崿F(xiàn)查詢松捉,每個(gè)服務(wù)操作都需要有唯一的流水號(hào)標(biāo)識(shí)夹界,也可使用此次服務(wù)操作對(duì)應(yīng)的資源ID來(lái)標(biāo)志,例如:請(qǐng)求流水號(hào)惩坑、訂單號(hào)等掉盅。

首先也拜,單筆查詢操作是必須提供的以舒,我們也鼓勵(lì)使用單筆訂單查詢,這是因?yàn)槊看握{(diào)用需要占用的負(fù)載是可控的慢哈,批量查詢則根據(jù)需要來(lái)提供蔓钟,如果使用了批量查詢,需要有合理的分頁(yè)機(jī)制卵贱,并且必須限制分頁(yè)的大小滥沫,以及對(duì)批量查詢的QPS需要有容量評(píng)估和流控等。

查詢模式的示意圖如下:

查詢模式

對(duì)于案例4:同步超時(shí)键俱、案例5:異步回調(diào)超時(shí)兰绣、案例6:掉單、案例7:系統(tǒng)間狀態(tài)不一致编振,我們都需要使用查詢模式來(lái)了解被調(diào)用服務(wù)的處理情況缀辩,來(lái)決定下一步做什么:補(bǔ)償未完成的操作還是回滾已經(jīng)完成的操作。

2. 補(bǔ)償模式

有了上面的查詢模式,在任何情況下臀玄,我們都能得知具體的操作所處的狀態(tài)瓢阴,如果整個(gè)操作處于不正常的狀態(tài),我們需要修正操作中有問(wèn)題的子操作健无,這可能需要重新執(zhí)行未完成的子操作荣恐,后者取消已經(jīng)完成的子操作,通過(guò)修復(fù)使整個(gè)分布式系統(tǒng)達(dá)到一致累贤,為了讓系統(tǒng)最終一致而做的努力都叫做補(bǔ)償叠穆。

對(duì)于服務(wù)化系統(tǒng)中同步調(diào)用的操作,業(yè)務(wù)操作發(fā)起的主動(dòng)方在還沒有得到業(yè)務(wù)操作執(zhí)行方的明確返回或者調(diào)用超時(shí)畦浓,場(chǎng)景可參考案例4:同步超時(shí)痹束,這個(gè)時(shí)候業(yè)務(wù)發(fā)起的主動(dòng)方需要及時(shí)的調(diào)用業(yè)務(wù)執(zhí)行方獲得操作執(zhí)行的狀態(tài),這里使用查詢模式讶请,獲得業(yè)務(wù)操作的執(zhí)行方的狀態(tài)后祷嘶,如果業(yè)務(wù)執(zhí)行方已經(jīng)完預(yù)設(shè)的工作,則業(yè)務(wù)發(fā)起方給業(yè)務(wù)的使用方返回成功夺溢,如果業(yè)務(wù)操作的執(zhí)行方的狀態(tài)為失敗或者未知论巍,則會(huì)立即告訴業(yè)務(wù)的使用方失敗,然后調(diào)用業(yè)務(wù)操作的逆向操作风响,保證操作不被執(zhí)行或者回滾已經(jīng)執(zhí)行的操作嘉汰,讓業(yè)務(wù)的使用方、業(yè)務(wù)發(fā)起的主動(dòng)方状勤、業(yè)務(wù)的操作方最終達(dá)成一致的狀態(tài)鞋怀。

補(bǔ)償模式的示意圖如下:

補(bǔ)償模式

補(bǔ)償操作根據(jù)發(fā)起形式分為:

  1. 自動(dòng)恢復(fù):程序根據(jù)發(fā)生不一致的環(huán)境,通過(guò)繼續(xù)未完成的操作持搜,或者回滾已經(jīng)完成的操作密似,自動(dòng)來(lái)達(dá)到一致
  1. 通知運(yùn)營(yíng):如果程序無(wú)法自動(dòng)恢復(fù),并且設(shè)計(jì)時(shí)考慮到了不一致的場(chǎng)景葫盼,可以提供運(yùn)營(yíng)功能残腌,通過(guò)運(yùn)營(yíng)手工進(jìn)行補(bǔ)償
  2. 通知技術(shù):如果很不巧,系統(tǒng)無(wú)法自動(dòng)回復(fù)贫导,又沒有運(yùn)營(yíng)功能抛猫,那必須通過(guò)技術(shù)手段來(lái)解決,技術(shù)手段包括走數(shù)據(jù)庫(kù)變更或者代碼變更來(lái)解決孩灯,這是最糟的一種場(chǎng)景

3. 異步確保模式

異步確保模式是補(bǔ)償模式的一個(gè)典型案例闺金,經(jīng)常應(yīng)用到使用方對(duì)響應(yīng)時(shí)間要求并不太高,我們通常把這類操作從主流程中摘除峰档,通過(guò)異步的方式進(jìn)行處理败匹,處理后把結(jié)果通過(guò)通知系統(tǒng)通知給使用方匣距,這個(gè)方案最大的好處能夠?qū)Ω卟l(fā)流量進(jìn)行消峰,例如:電商系統(tǒng)中的物流哎壳、配送毅待,以及支付系統(tǒng)中的計(jì)費(fèi)、入賬等归榕。

實(shí)踐中尸红,將要執(zhí)行的異步操作封裝后持久入庫(kù),然后通過(guò)定時(shí)撈取未完成的任務(wù)進(jìn)行補(bǔ)償操作來(lái)實(shí)現(xiàn)異步確保模式刹泄,只要定時(shí)系統(tǒng)足夠健壯外里,任何一個(gè)任務(wù)最終會(huì)被成功執(zhí)行。

異步確保模式的示意圖如下:

異步確保模式

對(duì)于案例5:異步回調(diào)超時(shí)特石,使用的就是異步確保模式盅蝗,這種情況下對(duì)于某個(gè)操作,如果遲遲沒有收到響應(yīng)姆蘸,我們通過(guò)查詢模式和補(bǔ)償模式來(lái)繼續(xù)未完成的操作墩莫。

4. 定期校對(duì)模式

既然我們?cè)谙到y(tǒng)中實(shí)現(xiàn)最終一致性,系統(tǒng)在沒有達(dá)到一致之前逞敷,系統(tǒng)間的狀態(tài)是不一致的狂秦,甚至是混亂的,需要補(bǔ)償操作來(lái)達(dá)到一致的目的推捐,但是我們?nèi)绾蝸?lái)發(fā)現(xiàn)需要補(bǔ)償?shù)牟僮髂兀?/p>

在操作的主流程中的系統(tǒng)間執(zhí)行校對(duì)操作裂问,我們可以事后異步的批量校對(duì)操作的狀態(tài),如果發(fā)現(xiàn)不一致的操作牛柒,則進(jìn)行補(bǔ)償堪簿,補(bǔ)償操作與補(bǔ)償模式中的補(bǔ)償操作是一致的。

另外皮壁,實(shí)現(xiàn)定期校對(duì)的一個(gè)關(guān)鍵就是分布式系統(tǒng)中需要有一個(gè)自始至終唯一的ID椭更,ID的生成請(qǐng)參考SnowFlake

在分布式系統(tǒng)中闪彼,全局唯一ID的示意圖如下:

唯一ID

一般情況下甜孤,生成全局唯一ID有兩種方法:

  1. 持久型:使用數(shù)據(jù)庫(kù)表自增字段或者Sequence生成协饲,為了提高效率畏腕,每個(gè)應(yīng)用節(jié)點(diǎn)可以緩存一批次的ID,如果機(jī)器重啟可能會(huì)損失一部分ID茉稠,但是這并不會(huì)產(chǎn)生任何問(wèn)題
  1. 時(shí)間型:一般由機(jī)器號(hào)描馅、業(yè)務(wù)號(hào)、時(shí)間而线、單節(jié)點(diǎn)內(nèi)自增ID組成铭污,由于時(shí)間一般精確到秒或者毫秒恋日,因此不需要持久就能保證在分布式系統(tǒng)中全局唯一、粗略遞增能特點(diǎn)

實(shí)踐中嘹狞,為了能在分布式系統(tǒng)中迅速的定位問(wèn)題岂膳,一般的分布式系統(tǒng)都有技術(shù)支持系統(tǒng),它能夠跟蹤一個(gè)請(qǐng)求的調(diào)用鏈磅网,調(diào)用鏈?zhǔn)窃诙S的維度跟蹤一個(gè)調(diào)用請(qǐng)求谈截,最后形成一個(gè)調(diào)用樹,原理可參考谷歌的論文Dapper, a Large-Scale Distributed Systems Tracing Infrastructure涧偷,一個(gè)開源的參考實(shí)現(xiàn)為pinpoint簸喂。

在分布式系統(tǒng)中,調(diào)用鏈的示意圖如下:

調(diào)用鏈

全局的唯一流水ID可以把一個(gè)請(qǐng)求在分布式系統(tǒng)中的流轉(zhuǎn)的路徑聚合燎潮,而調(diào)用鏈中的spanid可以把聚合的請(qǐng)求路徑通過(guò)樹形結(jié)構(gòu)進(jìn)行展示喻鳄,讓技術(shù)支持人員輕松的發(fā)現(xiàn)系統(tǒng)出現(xiàn)的問(wèn)題,能夠快速定位出現(xiàn)問(wèn)題的服務(wù)節(jié)點(diǎn)确封,提高應(yīng)急效率除呵。

關(guān)于訂單跟蹤、調(diào)用鏈跟蹤爪喘、業(yè)務(wù)鏈跟蹤竿奏,我們會(huì)在后續(xù)文章中詳細(xì)介紹。

在分布式系統(tǒng)中構(gòu)建了唯一ID腥放,調(diào)用鏈等基礎(chǔ)設(shè)施泛啸,我們很容易對(duì)系統(tǒng)間的不一致進(jìn)行核對(duì),通常我們需要構(gòu)建第三方的定期核對(duì)系統(tǒng)秃症,以第三方的角度來(lái)監(jiān)控服務(wù)執(zhí)行的健康程度候址。

定期核對(duì)系統(tǒng)示意圖如下:

定期核對(duì)模式

對(duì)于案例6:掉單、案例7:系統(tǒng)間狀態(tài)不一致通常通過(guò)定期校對(duì)模式發(fā)現(xiàn)問(wèn)題种柑,并通過(guò)補(bǔ)償模式來(lái)修復(fù)岗仑,最后完成系統(tǒng)間的最終一致性。

定期校對(duì)模式多應(yīng)用在金融系統(tǒng)聚请,金融系統(tǒng)由于涉及到資金安全荠雕,需要保證百分之百的準(zhǔn)確性,所以驶赏,需要多重的一致性保證機(jī)制炸卑,包括:系統(tǒng)間的一致性對(duì)賬、現(xiàn)金對(duì)賬煤傍、賬務(wù)對(duì)賬盖文、手續(xù)費(fèi)對(duì)賬等等,這些都屬于定期校對(duì)模式蚯姆,順便說(shuō)一下五续,金融系統(tǒng)與社交應(yīng)用在技術(shù)上本質(zhì)的區(qū)別在于社交應(yīng)用在于量大洒敏,而金融系統(tǒng)在于數(shù)據(jù)的準(zhǔn)確性。

到現(xiàn)在為止疙驾,我們看到通過(guò)查詢模式凶伙、補(bǔ)償模式、定期核對(duì)模式可以解決案例4到案例7的所有問(wèn)題它碎,對(duì)于案例4:同步超時(shí)镊靴,如果同步超時(shí),我們需要查詢狀態(tài)進(jìn)行補(bǔ)償链韭,對(duì)于案例5:異步回調(diào)超時(shí)偏竟,如果遲遲沒有收到回調(diào)響應(yīng),我們也會(huì)通過(guò)查詢狀態(tài)進(jìn)行補(bǔ)償敞峭,對(duì)于案例6:掉單踊谋、案例7:系統(tǒng)間狀態(tài)不一致,我們通過(guò)定期核對(duì)模式可以保證系統(tǒng)間操作的一致性旋讹,避免掉單和狀態(tài)不一致導(dǎo)致問(wèn)題殖蚕。

5. 可靠消息模式

在分布式系統(tǒng)中,對(duì)于主流程中優(yōu)先級(jí)比較低的操作沉迹,大多采用異步的方式執(zhí)行睦疫,也就是前面提到的異步確保型,為了讓異步操作的調(diào)用方和被調(diào)用方充分的解耦鞭呕,也由于專業(yè)的消息隊(duì)列本身具有可伸縮蛤育、可分片、可持久等功能葫松,我們通常通過(guò)消息隊(duì)列實(shí)現(xiàn)異步化瓦糕,對(duì)于消息隊(duì)列,我們需要建立特殊的設(shè)施保證可靠的消息發(fā)送以及處理機(jī)的冪等等腋么。

消息的可靠發(fā)送

消息的可靠發(fā)送可以認(rèn)為是盡最大努力發(fā)送消息通知咕娄,有兩種實(shí)現(xiàn)方法:

第一種,發(fā)送消息之前珊擂,把消息持久到數(shù)據(jù)庫(kù)圣勒,狀態(tài)標(biāo)記為待發(fā)送,然后發(fā)送消息摧扇,如果發(fā)送成功圣贸,將消息改為發(fā)送成功。定時(shí)任務(wù)定時(shí)從數(shù)據(jù)庫(kù)撈取一定時(shí)間內(nèi)未發(fā)送的消息扳剿,將消息發(fā)送旁趟。

消息發(fā)送模式1

第二種昼激,實(shí)現(xiàn)方式與第一種類似庇绽,不同的是持久消息的數(shù)據(jù)庫(kù)是獨(dú)立的锡搜,并不耦合在業(yè)務(wù)系統(tǒng)中。發(fā)送消息之前瞧掺,先發(fā)送一個(gè)預(yù)消息給某一個(gè)第三方的消息管理器耕餐,消息管理器將其持久到數(shù)據(jù)庫(kù),并標(biāo)記狀態(tài)為待發(fā)送辟狈,發(fā)送成功后肠缔,標(biāo)記消息為發(fā)送成功。定時(shí)任務(wù)定時(shí)從數(shù)據(jù)庫(kù)撈取一定時(shí)間內(nèi)未發(fā)送的消息哼转,回查業(yè)務(wù)系統(tǒng)是否要繼續(xù)發(fā)送明未,根據(jù)查詢結(jié)果來(lái)確定消息的狀態(tài)。

消息發(fā)送模式2

一些公司把消息的可靠發(fā)送實(shí)現(xiàn)在了中間件里壹蔓,通過(guò)Spring的注入趟妥,在消息發(fā)送的時(shí)候自動(dòng)持久消息記錄,如果有消息記錄沒有發(fā)送成功佣蓉,定時(shí)會(huì)補(bǔ)償發(fā)送披摄。

消息處理器的冪等性

如果我們要保證消息可靠的發(fā)送,簡(jiǎn)單來(lái)說(shuō)勇凭,要保證消息一定要發(fā)送出去疚膊,那么就需要有重試機(jī)制,有了重試機(jī)制虾标,消息一定會(huì)重復(fù)寓盗,那么我們需要對(duì)重復(fù)做處理。

處理重復(fù)的最佳方式為保證操作的冪等性璧函,冪等性的數(shù)學(xué)公式為:

f(f(x)) = f(x)

保證操作的冪等性常用的幾個(gè)方法:

  1. 使用數(shù)據(jù)庫(kù)表的唯一鍵進(jìn)行濾重贞让,拒絕重復(fù)的請(qǐng)求
  1. 使用分布式表對(duì)請(qǐng)求進(jìn)行濾重
  2. 使用狀態(tài)流轉(zhuǎn)的方向性來(lái)濾重,通常使用行級(jí)鎖來(lái)實(shí)現(xiàn)(后續(xù)在鎖相關(guān)的文章中詳細(xì)說(shuō)明)
  3. 根據(jù)業(yè)務(wù)的特點(diǎn)柳譬,操作本身就是冪等的喳张,例如:刪除一個(gè)資源、增加一個(gè)資源美澳、獲得一個(gè)資源等

6. 緩存一致性模型

大規(guī)模高并發(fā)系統(tǒng)中一個(gè)常見的核心需求就是億級(jí)的讀需求销部,顯然,關(guān)系型數(shù)據(jù)庫(kù)并不是解決高并發(fā)讀需求的最佳方案制跟,互聯(lián)網(wǎng)的經(jīng)典做法就是使用緩存抗讀需求舅桩,下面有一些使用緩存的保證一致性的最佳實(shí)踐:

  1. 如果性能要求不是非常的高,盡量使用分布式緩存雨膨,而不要使用本地緩存
  1. 種緩存的時(shí)候一定種完全擂涛,如果緩存數(shù)據(jù)的一部分有效,一部分無(wú)效聊记,寧可放棄種緩存撒妈,也不要把部分?jǐn)?shù)據(jù)種入緩存
  2. 數(shù)據(jù)庫(kù)與緩存只需要保持弱一致性恢暖,而不需要強(qiáng)一致性,讀的順序要先緩存狰右,后數(shù)據(jù)庫(kù)杰捂,寫的順序要先數(shù)據(jù)庫(kù)棺蛛,后緩存

這里的最佳實(shí)踐能夠解決案例8:緩存和數(shù)據(jù)庫(kù)不一致停局、案例9:本地緩存節(jié)點(diǎn)間不一致、案例10:緩存數(shù)據(jù)結(jié)構(gòu)不一致的問(wèn)題弧可,對(duì)于數(shù)據(jù)存儲(chǔ)層谷暮、緩存與數(shù)據(jù)庫(kù)蒿往、Nosql等的一致性是更深入的存儲(chǔ)一致性技術(shù),將會(huì)在后續(xù)文章單獨(dú)介紹湿弦,這里的數(shù)據(jù)一致性主要是處理應(yīng)用層與緩存熄浓、應(yīng)用層與數(shù)據(jù)庫(kù)、一部分的緩存與數(shù)據(jù)庫(kù)的一致性省撑。

3.5 專題模式

這一節(jié)介紹特殊場(chǎng)景下的一致性問(wèn)題和解決方案赌蔑。

遷移開關(guān)的設(shè)計(jì)

在大多數(shù)企業(yè)里,新項(xiàng)目和老項(xiàng)目一般會(huì)共存竟秫,大家都在努力的下掉老項(xiàng)目娃惯,但是由于種種原因總是下不掉,如果要徹底的下掉老項(xiàng)目肥败,就必須要有非常完善的遷移方案趾浅,遷移是一項(xiàng)非常復(fù)雜而艱巨的任務(wù),我會(huì)在將來(lái)的文章中詳細(xì)探討遷移方案馒稍、流程和技術(shù)皿哨,這里我們只對(duì)遷移中使用的開關(guān)進(jìn)行描述。

遷移過(guò)程必須使用開關(guān)纽谒,開關(guān)一般都會(huì)基于多個(gè)維度來(lái)設(shè)計(jì)证膨,例如:全局的、用戶的鼓黔、角色的央勒、商戶的、產(chǎn)品的等等澳化,如果遷移過(guò)程中遇到問(wèn)題崔步,我們需要關(guān)閉開關(guān),遷移回老的系統(tǒng)缎谷,這需要我們的新系統(tǒng)兼容老的數(shù)據(jù)井濒,老的系統(tǒng)也兼容新的數(shù)據(jù),從某種意義上來(lái)講,遷移比實(shí)現(xiàn)新系統(tǒng)更加困難瑞你。

曾經(jīng)看過(guò)很多簡(jiǎn)單的開關(guān)設(shè)計(jì)酪惭,有的開關(guān)設(shè)計(jì)在應(yīng)用層次,通過(guò)一個(gè)curl語(yǔ)句調(diào)用捏悬,沒有權(quán)限控制撞蚕,這樣的開關(guān)在服務(wù)池的每個(gè)節(jié)點(diǎn)都是不同步的润梯、不一致的过牙;還有的系統(tǒng)把開關(guān)配置放在中心化的配置系統(tǒng)、數(shù)據(jù)庫(kù)或者緩存等纺铭,處理的每個(gè)請(qǐng)求都通過(guò)統(tǒng)一的開關(guān)來(lái)判斷是否遷移等等寇钉,這樣的開關(guān)有一個(gè)致命的缺點(diǎn),服務(wù)請(qǐng)求在處理過(guò)程中舶赔,開關(guān)可能會(huì)變化扫倡,各個(gè)節(jié)點(diǎn)之間開關(guān)可能不同步、不一致竟纳,導(dǎo)致重復(fù)的請(qǐng)求可能走到新的邏輯又走了老的邏輯撵溃,如果新的邏輯和老的邏輯沒有保證冪等性,這個(gè)請(qǐng)求就被重復(fù)處理了锥累,如果是金融行業(yè)的應(yīng)用缘挑,可能會(huì)導(dǎo)致資金損失,電商系統(tǒng)可能會(huì)導(dǎo)致發(fā)貨并退款等問(wèn)題桶略。

這里面我們推薦使用訂單開關(guān)语淘,不管我們?cè)谑裁淳S度上設(shè)計(jì)了開關(guān),接收到服務(wù)請(qǐng)求后际歼,我們?cè)谡?qǐng)求創(chuàng)建的關(guān)聯(lián)實(shí)體(例如:訂單)上標(biāo)記開關(guān)惶翻,以后的任何處理流程,包括同步的和異步的處理流程鹅心,都通過(guò)訂單上的開關(guān)來(lái)判斷吕粗,而不是通過(guò)全局的或者基于配置的開關(guān),這樣在訂單創(chuàng)建的時(shí)候旭愧,開關(guān)已經(jīng)確定溯泣,不再變更,一旦一份數(shù)據(jù)不再發(fā)生變化榕茧,那么它永遠(yuǎn)是線程安全的垃沦,并且不會(huì)有不一致的問(wèn)題。

這個(gè)模式在生產(chǎn)中使用比較頻繁用押,建議每個(gè)企業(yè)都把這個(gè)模式作為設(shè)計(jì)評(píng)審的一項(xiàng)肢簿,如果不檢查這一項(xiàng),很多開發(fā)童鞋都會(huì)偷懶,直接在配置中或者數(shù)據(jù)庫(kù)中做個(gè)開關(guān)就上線了池充。

4 總結(jié)

本文從一致性問(wèn)題的實(shí)踐出發(fā)桩引,從大規(guī)模高并發(fā)服務(wù)化系統(tǒng)的實(shí)踐經(jīng)驗(yàn)中進(jìn)行總結(jié),列舉導(dǎo)致不一致的具體問(wèn)題收夸,圍繞著具體問(wèn)題坑匠,總結(jié)出解決不一致的方法,并且抽象成模式卧惜,供大家在開發(fā)服務(wù)化系統(tǒng)的過(guò)程中參考厘灼。

另外,由于篇幅有限咽瓷,還有一些關(guān)于分布式一致性的技術(shù)無(wú)法在一篇文章中與大家分享设凹,包括:paxos算法、raft算法茅姜、zab算法闪朱、nwr算法、一致性哈希等钻洒,我會(huì)在后續(xù)文章中詳細(xì)介紹奋姿。

5 反饋與建議


《分布式服務(wù)架構(gòu):原理、設(shè)計(jì)與實(shí)戰(zhàn)》是一本不可多得的理論與實(shí)踐相結(jié)合的架構(gòu)秘籍糯钙,京東購(gòu)買請(qǐng)點(diǎn)這里或者掃描下方二維碼粪狼。

《分布式服務(wù)架構(gòu):原理、設(shè)計(jì)與實(shí)戰(zhàn)》京東主頁(yè)

加入【云時(shí)代架構(gòu)】技術(shù)社區(qū)任岸,做互聯(lián)網(wǎng)時(shí)代最適合的架構(gòu)再榄,回歸架構(gòu)的簡(jiǎn)潔之美。

作者簡(jiǎn)書博客

云時(shí)代架構(gòu)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末享潜,一起剝皮案震驚了整個(gè)濱河市困鸥,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌剑按,老刑警劉巖疾就,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異艺蝴,居然都是意外死亡猬腰,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門猜敢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)姑荷,“玉大人盒延,你說(shuō)我怎么就攤上這事∈竺幔” “怎么了添寺?”我有些...
    開封第一講書人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)懈费。 經(jīng)常有香客問(wèn)我计露,道長(zhǎng),這世上最難降的妖魔是什么憎乙? 我笑而不...
    開封第一講書人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任票罐,我火速辦了婚禮,結(jié)果婚禮上寨闹,老公的妹妹穿的比我還像新娘胶坠。我一直安慰自己君账,他們只是感情好繁堡,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著乡数,像睡著了一般椭蹄。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上净赴,一...
    開封第一講書人閱讀 49,007評(píng)論 1 284
  • 那天绳矩,我揣著相機(jī)與錄音,去河邊找鬼玖翅。 笑死翼馆,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的金度。 我是一名探鬼主播应媚,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼猜极!你這毒婦竟也來(lái)了中姜?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤跟伏,失蹤者是張志新(化名)和其女友劉穎丢胚,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體受扳,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡携龟,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了勘高。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片峡蟋。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡浮定,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出层亿,到底是詐尸還是另有隱情桦卒,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布匿又,位于F島的核電站方灾,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏碌更。R本人自食惡果不足惜裕偿,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望痛单。 院中可真熱鬧嘿棘,春花似錦、人聲如沸旭绒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)挥吵。三九已至重父,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間忽匈,已是汗流浹背房午。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留丹允,地道東北人郭厌。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像雕蔽,于是被迫代替她去往敵國(guó)和親折柠。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容