微服務(wù)架構(gòu)數(shù)據(jù)一致性實現(xiàn)方案蛔添!

1. 傳統(tǒng)應(yīng)用的事務(wù)管理

1.1 本地事務(wù)

傳統(tǒng)單機應(yīng)用使用一個RDBMS作為數(shù)據(jù)源痰催,應(yīng)用開啟事務(wù),進(jìn)行CRUD迎瞧,提交或回滾事務(wù)夸溶,統(tǒng)統(tǒng)發(fā)生在本地事務(wù)中,由資源管理器(RM)直接提供事務(wù)支持凶硅。數(shù)據(jù)的一致性在一個本地事務(wù)中得到保證缝裁。

1.2 分布式事務(wù)

1.2.1 兩階段提交(2PC)

當(dāng)應(yīng)用逐漸擴(kuò)展,出現(xiàn)一個應(yīng)用使用多個數(shù)據(jù)源的情況足绅,這個時候本地事務(wù)已經(jīng)無法滿足數(shù)據(jù)一致性的要求压语。由于多個數(shù)據(jù)源的同時訪問,事務(wù)需要跨多個數(shù)據(jù)源管理编检,分布式事務(wù)應(yīng)運而生胎食。其中最流行的就是兩階段提交(2PC),分布式事務(wù)由事務(wù)管理器(TM)統(tǒng)一管理允懂。

兩階段提交分為準(zhǔn)備階段和提交階段厕怜。

兩階段提交-commit

兩階段提交-rollback

然而兩階段提交也不能完全保證數(shù)據(jù)一致性問題,并且有同步阻塞的問題蕾总,所以其優(yōu)化版本三階段提交(3PC)被發(fā)明了出來粥航。

1.2.2 三階段提交(3PC)

三階段提交

然而3PC也只能保證絕大多數(shù)情況下的數(shù)據(jù)一致性。

具體分布式事務(wù)2PC和3PC的詳細(xì)介紹請見 關(guān)于分布式事務(wù)生百、兩階段提交協(xié)議递雀、三階提交協(xié)議
。分布式事務(wù)不是本文的重點蚀浆,故不展開缀程。

2. 微服務(wù)下的事務(wù)管理

那么搜吧,分布式事務(wù)2PC或者3PC是否適合于微服務(wù)下的事務(wù)管理呢?答案是否定的杨凑,原因有三點:

  1. 由于微服務(wù)間無法直接進(jìn)行數(shù)據(jù)訪問滤奈,微服務(wù)間互相調(diào)用通常通過RPC(dubbo)或Http API(SpringCloud)進(jìn)行,所以已經(jīng)無法使用TM統(tǒng)一管理微服務(wù)的RM撩满。

  2. 不同的微服務(wù)使用的數(shù)據(jù)源類型可能完全不同蜒程,如果微服務(wù)使用了NoSQL之類不支持事務(wù)的數(shù)據(jù)庫,則事務(wù)根本無從談起伺帘。

  3. 即使微服務(wù)使用的數(shù)據(jù)源都支持事務(wù)昭躺,那么如果使用一個大事務(wù)將許多微服務(wù)的事務(wù)管理起來,這個大事務(wù)維持的時間伪嫁,將比本地事務(wù)長幾個數(shù)量級窍仰。如此長時間的事務(wù)及跨服務(wù)的事務(wù),將為產(chǎn)生很多鎖及數(shù)據(jù)不可用礼殊,嚴(yán)重影響系統(tǒng)性能。

由此可見针史,傳統(tǒng)的分布式事務(wù)已經(jīng)無法滿足微服務(wù)架構(gòu)下的事務(wù)管理需求晶伦。那么,既然無法滿足傳統(tǒng)的ACID事務(wù)啄枕,在微服務(wù)下的事務(wù)管理必然要遵循新的法則--BASE理論婚陪。

BASE理論由eBay的架構(gòu)師Dan
Pritchett提出,BASE理論是對CAP理論的延伸频祝,核心思想是即使無法做到強一致性泌参,應(yīng)用應(yīng)該可以采用合適的方式達(dá)到最終一致性。BASE是指基本可用(Basically
Available)常空、軟狀態(tài)( Soft State)沽一、最終一致性( Eventual Consistency)。

基本可用 :指分布式系統(tǒng)在出現(xiàn)故障的時候漓糙,允許損失部分可用性铣缠,即保證核心可用。

軟狀態(tài)
:允許系統(tǒng)存在中間狀態(tài)昆禽,而該中間狀態(tài)不會影響系統(tǒng)整體可用性蝗蛙。分布式存儲中一般一份數(shù)據(jù)至少會有三個副本,允許不同節(jié)點間副本同步的延時就是軟狀態(tài)的體現(xiàn)醉鳖。

最終一致性 :最終一致性是指系統(tǒng)中的所有數(shù)據(jù)副本經(jīng)過一定時間后捡硅,最終能夠達(dá)到一致的狀態(tài)。弱一致性和強一致性相反盗棵,最終一致性是弱一致性的一種特殊情況壮韭。

BASE中的 最終一致性
是對于微服務(wù)下的事務(wù)管理的根本要求北发,既基于微服務(wù)的事務(wù)管理無法達(dá)到強一致性,但必須保證最重一致性泰涂。那么鲫竞,有哪些方法可以保證微服務(wù)下的事務(wù)管理的最終一致性呢,按照實現(xiàn)原理分主要有兩類逼蒙,事件通知型和補償型从绘,其中事件通知型又可分為可靠事件通知模式及最大努力通知模式,而補償型又可分為TCC模式是牢、和業(yè)務(wù)補償模式兩種僵井。這四種模式都可以達(dá)到微服務(wù)下的數(shù)據(jù)最終一致性。

3. 實現(xiàn)微服務(wù)下數(shù)據(jù)一致性的方式

3.1 可靠事件通知模式

3.1.1 同步事件

可靠事件通知模式的設(shè)計理念比較容易理解驳棱,即是主服務(wù)完成后將結(jié)果通過事件(常常是消息隊列)傳遞給從服務(wù)批什,從服務(wù)在接受到消息后進(jìn)行消費,完成業(yè)務(wù)社搅,從而達(dá)到主服務(wù)與從服務(wù)間的消息一致性驻债。首先能想到的也是最簡單的就是同步事件通知,業(yè)務(wù)處理與消息發(fā)送同步執(zhí)行形葬,實現(xiàn)邏輯見下方代碼及時序圖合呐。

public void trans() {
        try {
        // 1. 操作數(shù)據(jù)庫
            bool result = dao.update(data);// 操作數(shù)據(jù)庫失敗,會拋出異常
        // 2. 如果數(shù)據(jù)庫操作成功則發(fā)送消息
            if(result){
                mq.send(data);// 如果方法執(zhí)行失敗笙以,會拋出異常
            }
        } catch (Exception e) {
            roolback();// 如果發(fā)生異常淌实,就回滾
        }
    }

上面的邏輯看上去天衣無縫,如果數(shù)據(jù)庫操作失敗則直接退出猖腕,不發(fā)送消息拆祈;如果發(fā)送消息失敗,則數(shù)據(jù)庫回滾倘感;如果數(shù)據(jù)庫操作成功且消息發(fā)送成功放坏,則業(yè)務(wù)成功,消息發(fā)送給下游消費老玛。然后仔細(xì)思考后轻姿,同步消息通知其實有兩點不足的地方。

  1. 在微服務(wù)的架構(gòu)下逻炊,有可能出現(xiàn)網(wǎng)絡(luò)IO問題或者服務(wù)器宕機的問題互亮,如果這些問題出現(xiàn)在時序圖的第7步,使得消息投遞后無法正常通知主服務(wù)(網(wǎng)絡(luò)問題)余素,或無法繼續(xù)提交事務(wù)(宕機)豹休,那么主服務(wù)將會認(rèn)為消息投遞失敗,會滾主服務(wù)業(yè)務(wù)桨吊,然而實際上消息已經(jīng)被從服務(wù)消費威根,那么就會造成主服務(wù)和從服務(wù)的數(shù)據(jù)不一致凤巨。具體場景可見下面兩張時序圖。
image
  1. 事件服務(wù)(在這里就是消息服務(wù))與業(yè)務(wù)過于耦合洛搀,如果消息服務(wù)不可用敢茁,會導(dǎo)致業(yè)務(wù)不可用。應(yīng)該將事件服務(wù)與業(yè)務(wù)解耦留美,獨立出來異步執(zhí)行彰檬,或者在業(yè)務(wù)執(zhí)行后先嘗試發(fā)送一次消息,如果消息發(fā)送失敗谎砾,則降級為異步發(fā)送逢倍。

3.1.2 異步事件

3.1.2.1 本地事件服務(wù)

為了解決3.1.1中描述的同步事件的問題,異步事件通知模式被發(fā)展了出來景图,既業(yè)務(wù)服務(wù)和事件服務(wù)解耦较雕,事件異步進(jìn)行,由單獨的事件服務(wù)保證事件的可靠投遞挚币。

異步事件通知-本地事件服務(wù)

當(dāng)業(yè)務(wù)執(zhí)行時亮蒋,在同一個本地事務(wù)中將事件寫入本地事件表,同時投遞該事件妆毕,如果事件投遞成功慎玖,則將該事件從事件表中刪除。如果投遞失敗设塔,則使用事件服務(wù)定時地異步統(tǒng)一處理投遞失敗的事件,進(jìn)行重新投遞远舅,直到事件被正確投遞闰蛔,并將事件從事件表中刪除。這種方式最大可能地保證了事件投遞的實效性图柏,并且當(dāng)?shù)谝淮瓮哆f失敗后序六,也能使用異步事件服務(wù)保證事件至少被投遞一次。

然而蚤吹,這種使用本地事件服務(wù)保證可靠事件通知的方式也有它的不足之處例诀,那便是業(yè)務(wù)仍舊與事件服務(wù)有一定耦合(第一次同步投遞時),更為嚴(yán)重的是裁着,本地事務(wù)需要負(fù)責(zé)額外的事件表的操作繁涂,為數(shù)據(jù)庫帶來了壓力,在高并發(fā)的場景二驰,由于每一個業(yè)務(wù)操作就要產(chǎn)生相應(yīng)的事件表操作扔罪,幾乎將數(shù)據(jù)庫的可用吞吐量砍了一半,這無疑是無法接受的桶雀。正是因為這樣的原因矿酵,可靠事件通知模式進(jìn)一步地發(fā)展-外部事件服務(wù)出現(xiàn)在了人們的眼中唬复。

3.1.2.2 外部事件服務(wù)

外部事件服務(wù)在本地事件服務(wù)的基礎(chǔ)上更進(jìn)了一步,將事件服務(wù)獨立出主業(yè)務(wù)服務(wù)全肮,主業(yè)務(wù)服務(wù)不在對事件服務(wù)有任何強依賴敞咧。

異步事件通知-外部事件服務(wù)

業(yè)務(wù)服務(wù)在提交前,向事件服務(wù)發(fā)送事件辜腺,事件服務(wù)只記錄事件休建,并不發(fā)送。業(yè)務(wù)服務(wù)在提交或回滾后通知事件服務(wù)哪自,事件服務(wù)發(fā)送事件或者刪除事件丰包。不用擔(dān)心業(yè)務(wù)系統(tǒng)在提交或者會滾后宕機而無法發(fā)送確認(rèn)事件給事件服務(wù),因為事件服務(wù)會定時獲取所有仍未發(fā)送的事件并且向業(yè)務(wù)系統(tǒng)查詢壤巷,根據(jù)業(yè)務(wù)系統(tǒng)的返回來決定發(fā)送或者刪除該事件邑彪。

外部事件雖然能夠?qū)I(yè)務(wù)系統(tǒng)和事件系統(tǒng)解耦,但是也帶來了額外的工作量:外部事件服務(wù)比起本地事件服務(wù)來說多了兩次網(wǎng)絡(luò)通信開銷(提交前胧华、提交/回滾后)寄症,同時也需要業(yè)務(wù)系統(tǒng)提供單獨的查詢接口給事件系統(tǒng)用來判斷未發(fā)送事件的狀態(tài)。

3.1.2.3 可靠事件通知模式的注意事項

可靠事件模式需要注意的有兩點矩动,1. 事件的正確發(fā)送有巧;2. 事件的重復(fù)消費。
通過異步消息服務(wù)可以確保事件的正確發(fā)送悲没,然而事件是有可能重復(fù)發(fā)送的篮迎,那么就需要消費端保證同一條事件不會重復(fù)被消費,簡而言之就是保證事件消費的 冪等性
示姿。

如果事件本身是具備冪等性的狀態(tài)型事件甜橱,如訂單狀態(tài)的通知(已下單、已支付栈戳、已發(fā)貨等)岂傲,則需要判斷事件的順序。一般通過時間戳來判斷子檀,既消費過了新的消息后镊掖,當(dāng)接受到老的消息直接丟棄不予消費。如果無法提供全局時間戳褂痰,則應(yīng)考慮使用全局統(tǒng)一的序列號亩进。

對于不具備冪等性的事件,一般是動作行為事件缩歪,如扣款100镐侯,存款200,則應(yīng)該將事件id及事件結(jié)果持久化,在消費事件前查詢事件id苟翻,若已經(jīng)消費則直接返回執(zhí)行結(jié)果韵卤;若是新消息,則執(zhí)行崇猫,并存儲執(zhí)行結(jié)果沈条。

3.2 最大努力通知模式

相比可靠事件通知模式,最大努力通知模式就容易理解多了诅炉。最大努力通知型的特點是蜡歹,業(yè)務(wù)服務(wù)在提交事務(wù)后,進(jìn)行有限次數(shù)(設(shè)置最大次數(shù)限制)的消息發(fā)送涕烧,比如發(fā)送三次消息月而,若三次消息發(fā)送都失敗,則不予繼續(xù)發(fā)送议纯。所以有可能導(dǎo)致消息的丟失父款。同時,主業(yè)務(wù)方需要提供查詢接口給從業(yè)務(wù)服務(wù)瞻凤,用來恢復(fù)丟失消息憨攒。最大努力通知型對于時效性保證比較差(既可能會出現(xiàn)較長時間的軟狀態(tài)),所以對于數(shù)據(jù)一致性的時效性要求比較高的系統(tǒng)無法使用阀参。這種模式通常使用在不同業(yè)務(wù)平臺服務(wù)或者對于第三方業(yè)務(wù)服務(wù)的通知肝集,如銀行通知、商戶通知等蛛壳,這里不再展開杏瞻。

3.3 業(yè)務(wù)補償模式

接下來介紹兩種補償模式,補償模式比起事件通知模式最大的不同是衙荐,補償模式的上游服務(wù)依賴于下游服務(wù)的運行結(jié)果捞挥,而事件通知模式上游服務(wù)不依賴于下游服務(wù)的運行結(jié)果。首先介紹業(yè)務(wù)補償模式赫模,業(yè)務(wù)補償模式是一種純補償模式树肃,其設(shè)計理念為蒸矛,業(yè)務(wù)在調(diào)用的時候正常提交瀑罗,當(dāng)一個服務(wù)失敗的時候,所有其依賴的上游服務(wù)都進(jìn)行業(yè)務(wù)補償操作雏掠。舉個例子斩祭,小明從杭州出發(fā),去往美國紐約出差乡话,現(xiàn)在他需要定從杭州去往上海的火車票摧玫,以及從上海飛往紐約的飛機票。如果小明成功購買了火車票之后發(fā)現(xiàn)那天的飛機票已經(jīng)售空了,那么與其在上海再多待一天诬像,小明還不如取消去上海的火車票屋群,選擇飛往北京再轉(zhuǎn)機紐約,所以小明就取消了去上海的火車票坏挠。這個例子中購買杭州到上海的火車票是服務(wù)a芍躏,購買上海到紐約的飛機票是服務(wù)b,業(yè)務(wù)補償模式就是在服務(wù)b失敗的時候降狠,對服務(wù)a進(jìn)行補償操作对竣,在例子中就是取消杭州到上海的火車票。

補償模式要求每個服務(wù)都提供補償借口榜配,且這種補償一般來說是 不完全補償
否纬,既即使進(jìn)行了補償操作,那條取消的火車票記錄還是一直存在數(shù)據(jù)庫中可以被追蹤(一般是有相信的狀態(tài)字段“已取消”作為標(biāo)記)蛋褥,畢竟已經(jīng)提交的線上數(shù)據(jù)一般是不能進(jìn)行物理刪除的临燃。

業(yè)務(wù)補償模式最大的缺點是軟狀態(tài)的時間比較長,既數(shù)據(jù)一致性的時效性很低壁拉,多個服務(wù)常趁恚可能處于數(shù)據(jù)不一致的情況。

3.4 TCC/Try Confirm Cancel模式

TCC模式是一種優(yōu)化了的業(yè)務(wù)補償模式弃理,它可以做到 完全補償
溃论,既進(jìn)行補償后不留下補償?shù)募o(jì)錄,就好像什么事情都沒有發(fā)生過一樣痘昌。同時钥勋,TCC的軟狀態(tài)時間很短,原因是因為TCC是一種兩階段型模式(已經(jīng)忘了兩階段概念的可以回顧一下1.2.1)辆苔,只有在所有的服務(wù)的第一階段(try)都成功的時候才進(jìn)行第二階段確認(rèn)(Confirm)操作算灸,否則進(jìn)行補償(Cancel)操作,而在try階段是不會進(jìn)行真正的業(yè)務(wù)處理的驻啤。

TCC模式

TCC模式的具體流程為兩個階段:

  1. Try菲驴,業(yè)務(wù)服務(wù)完成所有的業(yè)務(wù)檢查,預(yù)留必需的業(yè)務(wù)資源

  2. 如果Try在所有服務(wù)中都成功骑冗,那么執(zhí)行Confirm操作赊瞬,Confirm操作不做任何的業(yè)務(wù)檢查(因為try中已經(jīng)做過),只是用Try階段預(yù)留的業(yè)務(wù)資源進(jìn)行業(yè)務(wù)處理贼涩;否則進(jìn)行Cancel操作巧涧,Cancel操作釋放Try階段預(yù)留的業(yè)務(wù)資源。

這么說可能比較模糊遥倦,下面我舉一個具體的例子谤绳,小明在線從招商銀行轉(zhuǎn)賬100元到廣發(fā)銀行。這個操作可看作兩個服務(wù),服務(wù)a從小明的招行賬戶轉(zhuǎn)出100元缩筛,服務(wù)b從小明的廣發(fā)銀行帳戶匯入100元消略。

服務(wù)a(小明從招行轉(zhuǎn)出100元):

try: update cmb_account set balance=balance-100, freeze=freeze+100 where
acc_id=1 and balance>100;

confirm: update cmb_account set freeze=freeze-100 where acc_id=1;

cancel: update cmb_account set balance=balance+100, freeze=freeze-100 where
acc_id=1;

服務(wù)b(小明往廣發(fā)銀行匯入100元):

try: update cgb_account set freeze=freeze+100 where acc_id=1;

confirm: update cgb_account set balance=balance+100, freeze=freeze-100 where
acc_id=1;

cancel: update cgb_account set freeze=freeze-100 where acc_id=1;

具體說明

  • a的try階段,服務(wù)做了兩件事瞎抛,1:業(yè)務(wù)檢查疑俭,這里是檢查小明的帳戶里的錢是否多余100元;2:預(yù)留資源婿失,將100元從余額中劃入凍結(jié)資金。

  • a的confirm階段豪硅,這里不再進(jìn)行業(yè)務(wù)檢查哩照,因為try階段已經(jīng)做過了飘弧,同時由于轉(zhuǎn)賬已經(jīng)成功次伶,將凍結(jié)資金扣除。

  • a的cancel階段舌镶,釋放預(yù)留資源哟楷,既100元凍結(jié)資金墨技,并恢復(fù)到余額健提。

  • b的try階段進(jìn)行私痹,預(yù)留資源紊遵,將100元凍結(jié)匀奏。

  • b的confirm階段,使用try階段預(yù)留的資源,將100元凍結(jié)資金劃入余額瘫寝。

  • b的cancel階段栽惶,釋放try階段的預(yù)留資源位仁,將100元從凍結(jié)資金中減去。

從上面的簡單例子可以看出察纯,TCC模式比純業(yè)務(wù)補償模式更加復(fù)雜,所以在實現(xiàn)上每個服務(wù)都需要實現(xiàn)Cofirm和Cancel兩個接口葛峻。

3.5 總結(jié)

下面的表格對這四種常用的模式進(jìn)行了比較:

四種常用的模式比較
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市政勃,隨后出現(xiàn)的幾起案子唧龄,更是在濱河造成了極大的恐慌,老刑警劉巖奸远,帶你破解...
    沈念sama閱讀 219,110評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件既棺,死亡現(xiàn)場離奇詭異,居然都是意外死亡懒叛,警方通過查閱死者的電腦和手機丸冕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來薛窥,“玉大人胖烛,你說我怎么就攤上這事∽缑裕” “怎么了佩番?”我有些...
    開封第一講書人閱讀 165,474評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長罢杉。 經(jīng)常有香客問我趟畏,道長,這世上最難降的妖魔是什么滩租? 我笑而不...
    開封第一講書人閱讀 58,881評論 1 295
  • 正文 為了忘掉前任赋秀,我火速辦了婚禮利朵,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘沃琅。我一直安慰自己,他們只是感情好蜘欲,可當(dāng)我...
    茶點故事閱讀 67,902評論 6 392
  • 文/花漫 我一把揭開白布益眉。 她就那樣靜靜地躺著,像睡著了一般姥份。 火紅的嫁衣襯著肌膚如雪郭脂。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,698評論 1 305
  • 那天澈歉,我揣著相機與錄音展鸡,去河邊找鬼。 笑死埃难,一個胖子當(dāng)著我的面吹牛莹弊,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播涡尘,決...
    沈念sama閱讀 40,418評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼忍弛,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了考抄?” 一聲冷哼從身側(cè)響起细疚,我...
    開封第一講書人閱讀 39,332評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎川梅,沒想到半個月后疯兼,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,796評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡贫途,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,968評論 3 337
  • 正文 我和宋清朗相戀三年吧彪,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片丢早。...
    茶點故事閱讀 40,110評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡来氧,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出香拉,到底是詐尸還是另有隱情啦扬,我是刑警寧澤,帶...
    沈念sama閱讀 35,792評論 5 346
  • 正文 年R本政府宣布凫碌,位于F島的核電站扑毡,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏盛险。R本人自食惡果不足惜瞄摊,卻給世界環(huán)境...
    茶點故事閱讀 41,455評論 3 331
  • 文/蒙蒙 一勋又、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧换帜,春花似錦楔壤、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至祟牲,卻和暖如春隙畜,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背说贝。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評論 1 272
  • 我被黑心中介騙來泰國打工议惰, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人乡恕。 一個月前我還...
    沈念sama閱讀 48,348評論 3 373
  • 正文 我出身青樓言询,卻偏偏與公主長得像,于是被迫代替她去往敵國和親傲宜。 傳聞我的和親對象是個殘疾皇子倍试,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,047評論 2 355

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