如何組裝一個注冊中心

hello,大家好呀烂翰,我是小樓夯缺。今天不寫B(tài)UG,來聊一聊注冊中心甘耿。

標(biāo)題本來想叫《如何設(shè)計一個注冊中心》踊兜,但網(wǎng)上已經(jīng)有好多類似標(biāo)題的文章了。所以打算另辟蹊徑佳恬,換個角度捏境,如何組裝一個注冊中心。

組裝意味著不必從0開始造輪子毁葱,這也比較符合許多公司對待自研基礎(chǔ)組件的態(tài)度垫言。

知道如何組裝一個注冊中心有什么用呢?

第一可以更深入理解注冊中心倾剿。以我個人經(jīng)歷來說筷频,注冊中心的第一印象就是Dubbo的Zookeeper(以下簡稱zk),后來逐漸深入前痘,學(xué)會了如何去zk上查看Dubbo注冊的數(shù)據(jù)凛捏,并能排查一些問題。后來了解了Nacos芹缔,才發(fā)現(xiàn)坯癣,原來注冊中心還可以如此簡單,再后來一直從事服務(wù)發(fā)現(xiàn)相關(guān)工作最欠,對一些細(xì)枝末節(jié)也有了一些新的理解示罗。

第二可以學(xué)習(xí)技術(shù)選型的方法蓬网,注冊中心中的每個模塊,都會在不同的需求下有不同的選擇鹉勒,最終的選擇取決于對需求的把握以及技術(shù)視野帆锋,但這兩項是內(nèi)功,一時半會練不成禽额,學(xué)個選型的方法還是可以的锯厢。

本文打算從需求分析開始,一步步拆解各個模塊脯倒,整個注冊中心以一種如無必要实辑,勿增實體的原則進(jìn)行組裝,但也不會是個玩具藻丢,向生產(chǎn)可用對齊剪撬。

當(dāng)然在實際項目中,不建議重復(fù)造輪子悠反,盡量用現(xiàn)成的解決方案残黑,所以本文僅供學(xué)習(xí)參考。

需求分析

本文的注冊中心需求很簡單斋否,就三點:可注冊梨水、能發(fā)現(xiàn)高可用茵臭。

服務(wù)的注冊和發(fā)現(xiàn)是注冊中心的基本功能疫诽,高可用則是生產(chǎn)環(huán)境的基本要求,如果高可用不要求旦委,那本文可講解的內(nèi)容就很少奇徒,上圖中的高可用標(biāo)注只是個示意,高可用在很多方面都有體現(xiàn)缨硝。

至于其他花里胡哨的功能摩钙,我們暫且不表。

我們這里介紹三個角色追葡,后文以此為基礎(chǔ):

  • 提供者(Provider):服務(wù)的提供方(被調(diào)用方)
  • 消費者(Consumer):服務(wù)的消費方(調(diào)用方)
  • 注冊中心(Registry):本文主角腺律,服務(wù)提供列表奕短、消費關(guān)系等數(shù)據(jù)的存儲方

接口定義

注冊中心和客戶端(SDK)的交互接口有三個:

  • 注冊(register)宜肉,將服務(wù)提供方注冊到注冊中心
  • 注銷(unregister),將注冊的服務(wù)從注冊中心中刪除
  • 訂閱(subscribe)翎碑,服務(wù)消費方訂閱需要的服務(wù)谬返,訂閱后提供方有變更將通知到對應(yīng)的消費方

注冊、注銷可以是服務(wù)提供方的進(jìn)程發(fā)起日杈,也可以是其他的旁路程序輔助發(fā)起遣铝,比如發(fā)布系統(tǒng)在發(fā)布一臺機器完成后佑刷,可調(diào)用注冊接口,將其注冊到注冊中心酿炸,注銷也是類似流程瘫絮,但這種方式并不多見,而且如果只考慮實現(xiàn)一個注冊中心填硕,必然是可以單獨運行的麦萤,所以通常注冊、注銷由提供方進(jìn)程負(fù)責(zé)扁眯。

有了這三個接口壮莹,我們該如何去定義接口呢?注冊服務(wù)到底有哪些字段需要注冊姻檀?訂閱需要傳什么字段命满?以什么序列化方式?用什么協(xié)議傳輸绣版?

這些問題接踵而來胶台,我覺得我們先不急著去做選擇,先看看這個領(lǐng)域有沒有相關(guān)標(biāo)準(zhǔn)杂抽,如果有就參考或者直接按照標(biāo)準(zhǔn)實現(xiàn)概作,如果沒有,再來分析每一點的選擇默怨。

服務(wù)發(fā)現(xiàn)還真有一套標(biāo)準(zhǔn)讯榕,但又不完全有。它叫OpenSergo匙睹,它其實是服務(wù)治理的一套標(biāo)準(zhǔn)愚屁,包含了服務(wù)發(fā)現(xiàn):

OpenSergo 是一套開放、通用的痕檬、面向分布式服務(wù)架構(gòu)霎槐、覆蓋全鏈路異構(gòu)化生態(tài)的服務(wù)治理標(biāo)準(zhǔn),基于業(yè)界服務(wù)治理場景與實踐形成通用標(biāo)準(zhǔn)規(guī)范梦谜。OpenSergo 的最大特點就是以統(tǒng)一的一套配置/DSL/協(xié)議定義服務(wù)治理規(guī)則丘跌,面向多語言異構(gòu)化架構(gòu),做到全鏈路生態(tài)覆蓋唁桩。無論微服務(wù)的語言是 Java, Go, Node.js 還是其它語言闭树,無論是標(biāo)準(zhǔn)微服務(wù)還是 Mesh 接入,從網(wǎng)關(guān)到微服務(wù)荒澡,從數(shù)據(jù)庫到緩存报辱,從服務(wù)注冊發(fā)現(xiàn)到配置,開發(fā)者都可以通過同一套 OpenSergo CRD 標(biāo)準(zhǔn)配置針對每一層進(jìn)行統(tǒng)一的治理管控单山,而無需關(guān)注各框架碍现、語言的差異點幅疼,降低異構(gòu)化、全鏈路服務(wù)治理管控的復(fù)雜度昼接。

官網(wǎng):https://opensergo.io/

我們需要的服務(wù)注冊與發(fā)現(xiàn)也被納入其中:

說有但也不是完全有是因為這個標(biāo)準(zhǔn)還在建設(shè)中爽篷,服務(wù)發(fā)現(xiàn)相關(guān)的標(biāo)準(zhǔn)在寫這篇文章的時候還沒有給出。

既然沒有標(biāo)準(zhǔn)慢睡,可以結(jié)合現(xiàn)有的系統(tǒng)以及經(jīng)驗來定義狼忱,這里我用json的序列化方式給出,以下為筆者的總結(jié)一睁,不能囊括所有情形钻弄,需要時根據(jù)業(yè)務(wù)適當(dāng)做一些調(diào)整:

  1. 服務(wù)注冊入?yún)?/li>
{
  "application":"provider_test", // 應(yīng)用名
  "protocol":"http", // 協(xié)議
  "addr":"127.0.0.1:8080", // 提供方的地址
  "meta":{ // 攜帶的元數(shù)據(jù),以下三個為示例
    "cluster":"small",
    "idc":"shanghai",
    "tag":"read"
  }
}
  1. 服務(wù)訂閱入?yún)?/li>
{
    "subscribes":[
        {
            "provider":"test_provider1", // 訂閱的應(yīng)用名
            "protocol":"http", // 訂閱的協(xié)議
            "meta":{ // 攜帶的元數(shù)據(jù)者吁,以下為示例
                "cluster":"small",
                "idc":"shanghai",
                "tag":"read"
            }
        },
        {
            "provider":"test_provider2",
            "protocol":"http",
            "meta":{
                "cluster":"small",
                "tag":"read"
            }
        }
    ]
}
  1. 服務(wù)發(fā)現(xiàn)出參
{
    "version":"23des4f", // 版本
    "endpoints":[ // 實例
        {
            "application":"provider_test",
            "protocol":"http",
            "addr":"127.0.0.1:8080",
            "meta":{
                "cluster":"small",
                "idc":"shanghai",
                "tag":"read"
            }
        },
        {
            "application":"provider_test",
            "protocol":"http",
            "addr":"127.0.0.2:8080",
            "meta":{
                "cluster":"small",
                "idc":"shanghai",
                "tag":"read"
            }
        }
    ]
}

變更推送 & 服務(wù)健康檢查

有了定義窘俺,我們?nèi)绾芜x擇序列化方式?選擇序列化方式有兩個重要參考點:

  • 語言的適配程度复凳,比如 json 幾乎所有編程語言都能適配瘤泪。除非能非常確定5-10年內(nèi)不會有多語言的需求,否則我還是非常建議你選擇一個跨語言的序列化協(xié)議
  • 性能育八,序列化的性能包含了兩層意思对途,序列化的速度(cpu消耗)與序列化后的體積,設(shè)想一個場景髓棋,一個服務(wù)被非常多的應(yīng)用訂閱实檀,如果此時該服務(wù)發(fā)布,則會觸發(fā)非常龐大的推送事件按声,此時注冊中心的cpu和網(wǎng)絡(luò)則有可能被打滿膳犹,導(dǎo)致服務(wù)不可用

至于編程語言的選擇,我覺得應(yīng)該更加偏向團(tuán)隊對語言的掌握签则,以能hold住為最主要须床,這點沒什么好說的,一般也只會在 Java / Go 中去選渐裂,很少見用其他語言實現(xiàn)的注冊中心豺旬。

對于注冊、訂閱接口柒凉,無論是基于TCP的自定義私有協(xié)議族阅,還是用HTTP協(xié)議,甚至基于HTTP2的gRPC我覺得都可以扛拨。

但變更推送這個技術(shù)點的實現(xiàn)耘分,有多種實現(xiàn)方式:

  1. 定時輪詢举塔,每隔一段時間向注冊中心請求查詢訂閱的服務(wù)提供列表
  2. 長輪詢绑警,向注冊中心查詢訂閱的服務(wù)提供列表求泰,如果列表較上次沒有變化,則服務(wù)端hold住請求计盒,等待有變化或者超時(較長時間)才返回
  3. UDP推送渴频,服務(wù)列表有變化時通過UDP將事件通知給客戶端,但UDP推送不一定可靠北启,可能會丟失卜朗、亂序,故要配合定時輪詢(較長時間間隔)來作為一個兜底
  4. TCP長連接推送咕村,客戶端與注冊中心建立一個TCP長連接场钉,有變更時推送給客戶端

從實現(xiàn)的難易、實時性懈涛、資源消耗三個方面來比較這四種實現(xiàn)方式:

實現(xiàn)難易 實時性 資源消耗 備注
定時輪詢 簡單 實時性越高逛万,資源消耗越多
長輪詢 中等 中等 服務(wù)端hold住很多請求
UDP推送 中等 推送可能丟失,需要配合定時輪詢(間隔較長)
TCP長連接推送 中等 中等 服務(wù)端需要保持很多長連接

似乎我們不好抉擇到底使用哪種方式來做推送批钠,但以我自己的經(jīng)驗來看宇植,定時輪詢應(yīng)該首先被排除,因為即便是一個初具規(guī)模的公司埋心,定時輪詢的消耗也是巨大的指郁,更何況這種消耗隨著實時性以及服務(wù)的規(guī)模日漸龐大,最后變得不可維護(hù)拷呆。

剩下三種方案都可以選擇闲坎,我們可以繼續(xù)結(jié)合服務(wù)節(jié)點的健康檢查來綜合判斷。

服務(wù)啟動時注冊到注冊中心茬斧,當(dāng)服務(wù)停止時箫柳,從注冊中心摘除,通常摘除會借助劫持kill信號實現(xiàn)啥供,如果是Java則有封裝好的ShutdownHook悯恍,當(dāng)進(jìn)程被 kill 時,觸發(fā)劫持邏輯伙狐,從注冊中心摘除涮毫,實現(xiàn)優(yōu)雅退出。

但事情不總是如預(yù)期贷屎,如果有人執(zhí)行了kill -9強制殺死進(jìn)程罢防,或者機器出現(xiàn)硬件故障,會導(dǎo)致提供者還在注冊中心唉侄,但已無法提供服務(wù)咒吐。

此時需要一種健康檢查機制來確保服務(wù)宕機時,消費者能正常感知,從而切走流量恬叹,保證線上服務(wù)的穩(wěn)定性候生。

關(guān)于健康檢查機制,在之前的文章《服務(wù)探活的五種方式》中有專門的總結(jié)绽昼,這里也列舉一下唯鸭,以便做出正確的選擇:

優(yōu)點 缺點
消費者被動探活 不依賴注冊中心 需在服務(wù)調(diào)用處實現(xiàn)邏輯;用真實流量探測硅确,可能會有滯后性
消費者主動探活 不依賴注冊中心 需在服務(wù)調(diào)用處實現(xiàn)邏輯
提供者上報心跳 對調(diào)用無入侵 需消費者服務(wù)發(fā)現(xiàn)模塊實現(xiàn)邏輯目溉,服務(wù)端處理心跳消耗資源大
注冊中心主動探測 對客戶端無要求 資源消耗大,實時性不高
提供者與注冊中心會話保持 實時性好菱农,資源消耗少 與注冊中心需保持TCP長連接

我們暫時無法控制調(diào)用動作缭付,故而前2項依賴消費者的方案排除,提供者上報心跳如果規(guī)模較小還好循未,上點規(guī)模也會不堪重任蛉腌,這點在Nacos中就體現(xiàn)了,Nacos 1.x版本使用提供者上報心跳的方式保持服務(wù)健康狀態(tài)只厘,由于每次上報健康狀態(tài)都需要寫入數(shù)據(jù)(最后健康檢查時間)烙丛,故對資源的消耗是非常大的,所以Nacos 2.0版本后就改為了長連接會話保持健康狀態(tài)羔味。

所以健康檢查我個人比較傾向最后兩種方案:注冊中心主動探測提供者與注冊中心會話保持的方式河咽。

結(jié)合上述變更推送,我們發(fā)現(xiàn)如果實現(xiàn)了長連接赋元,好處將很多忘蟹,很多情況下,一個服務(wù)既是消費者搁凸,又是提供者媚值,此時一條TCP長連接可以解決推送和健康檢查,甚至在注冊注銷接口的實現(xiàn)护糖,我們也可以復(fù)用這條連接褥芒,可謂是一石三鳥。

長連接技術(shù)選型

長連接的技術(shù)選型嫡良,在《Nacos架構(gòu)與原理》這本電子書中有有詳細(xì)的介紹锰扶,我覺得這部分堪稱技術(shù)選型的典范,我們參考下寝受,本節(jié)內(nèi)容大量參考《Nacos架構(gòu)與原理》坷牛,如有雷同,那便是真是雷同很澄。

首先是長連接的核心訴求:

圖來自《Nacos架構(gòu)與原理》

  • 低成本快速感知:客戶端需要在服務(wù)端不可用時盡快地切換到新的服務(wù)節(jié)點京闰,降低不可用時間
    • 客戶端正常重啟:客戶端主動關(guān)閉連接颜及,服務(wù)端實時感知
    • 服務(wù)端正常重啟 : 服務(wù)端主動關(guān)閉連接,客戶端實時感知
  • 防抖:網(wǎng)絡(luò)短暫不可用蹂楣,客戶端需要能接受短暫網(wǎng)絡(luò)抖動俏站,需要一定重試機制,防止集群抖動捐迫,超過閾值后需要自動切換 server乾翔,但要防止請求風(fēng)暴
  • 斷網(wǎng):斷網(wǎng)場景下爱葵,以合理的頻率進(jìn)行重試施戴,斷網(wǎng)結(jié)束時可以快速重連恢復(fù)
  • 低成本多語言實現(xiàn):在客戶端層面要盡可能多的支持多語言,降低多 語言實現(xiàn)成本
  • 開源社區(qū):文檔萌丈,開源社區(qū)活躍度赞哗,使用用戶數(shù)等,面向未來是否有足夠的支持度

據(jù)此辆雾,我們可選的輪子有:

gRPC Rsocket Netty Mina
客戶端感知斷連 基于 stream 流 error complete 事件可實現(xiàn) 支持 支持 支持
服務(wù)端感知斷連 支持 支持 支持 支持
心跳狈舅瘢活 應(yīng)用層自定義,ping-pong 消息 自定義 kee palive frame TCP+ 自定義 自定義 kee palive filter
多語言支持 一般 只Java 只Java

我比較傾向gRPC度迂,而且gRPC的社區(qū)活躍度要強于Rsocket藤乙。

數(shù)據(jù)存儲

注冊中心數(shù)據(jù)存儲方案,大致可分為2類:

  • 利用第三方組件完成惭墓,如Mysql坛梁、Redis等,好處是有現(xiàn)成的水平擴(kuò)容方案腊凶,穩(wěn)定性強划咐;壞處是架構(gòu)變得復(fù)雜
  • 利用注冊中心本身來存儲數(shù)據(jù),好處是無需引入額外組件钧萍;壞處是需要解決穩(wěn)定性問題

第一種方案我們不必多說褐缠,第二種方案中最關(guān)鍵的就是解決數(shù)據(jù)在注冊中心各節(jié)點之間的同步,因為在數(shù)據(jù)存儲在注冊中心本身節(jié)點上风瘦,如果是單機队魏,機器故障或者掛掉,數(shù)據(jù)存在丟失風(fēng)險万搔,所以必須得有副本器躏。

數(shù)據(jù)不能丟失,這點必須要保證蟹略,否則穩(wěn)定性就無從談起了登失。保證數(shù)據(jù)不丟失怎么理解?在客戶端向注冊中心發(fā)起注冊請求后挖炬,收到正常的響應(yīng)揽浙,這就意味著數(shù)據(jù)存儲了起來,除非所有注冊中心節(jié)點故障,否則數(shù)據(jù)就一定要存在馅巷。

如下圖膛虫,比如提供者往一個節(jié)點注冊數(shù)據(jù)后,正常響應(yīng)钓猬,但是數(shù)據(jù)同步是異步的稍刀,在同步完成前,nodeA節(jié)點就掛掉敞曹,則這條注冊數(shù)據(jù)就丟失了账月。

所以,我們要極力避免這種情況澳迫。

而一致性算法(如raft)就解決了這個問題局齿,一致性算法能保證大部分節(jié)點是正常的情況下,能對外提供一致的數(shù)據(jù)服務(wù)橄登,但犧牲了性能和可用性抓歼,raft算法在選主時便不能對外提供服務(wù)。

有沒有退而求其次的算法呢拢锹?還真有谣妻,像Nacos、Eureka提供的AP模型卒稳,他們的核心點在于客戶端可以recover數(shù)據(jù)蹋半,也就是注冊中心追求最終一致性,如果某些數(shù)據(jù)丟失展哭,服務(wù)提供方是可以重新將數(shù)據(jù)注冊上來湃窍。

比如我們將提供方與注冊中心之間設(shè)計為長連接,提供方注冊服務(wù)后匪傍,連接的節(jié)點還沒來得及將數(shù)據(jù)同步到其他節(jié)點就掛了您市,此時提供方的連接也會斷開,當(dāng)連接重新建立時役衡,服務(wù)提供方可以重新注冊茵休,恢復(fù)注冊中心的數(shù)據(jù)。

對于注冊中心選用AP手蝎、還是CP模型榕莺,業(yè)界早有爭論,但也基本達(dá)成了共識棵介,AP要優(yōu)于CP钉鸯,因為數(shù)據(jù)不一致總比不可用要好吧?你說是不是邮辽。

高可用

其實高可用的設(shè)計散落在各個細(xì)節(jié)點唠雕,如上文提到的數(shù)據(jù)存儲贸营,其基本要求就是高可用。除此之外岩睁,我們的設(shè)計也都必須是面向失敗的設(shè)計钞脂。

假設(shè)我們的服務(wù)器會全部掛掉,怎樣才能保持服務(wù)間的調(diào)用不受影響捕儒?

通常注冊中心不侵入服務(wù)調(diào)用冰啃,而是在內(nèi)存(或磁盤)中緩存一份服務(wù)列表,當(dāng)注冊中心完全掛了刘莹,大不了這份緩存不再更新阎毅,但也不影響現(xiàn)有的服務(wù)調(diào)用,但新應(yīng)用啟動就會受到影響栋猖。

總結(jié)

本文內(nèi)容略多净薛,用一幅圖來總結(jié):

組裝一個線上可用的注冊中心最小集汪榔,從需求分析出發(fā)蒲拉,每一步都有許多選擇,本文通過一些核心的技術(shù)選型來描繪出一個大致藍(lán)圖痴腌,剩下的工作就是用代碼將這些組裝起來雌团。

其中有些細(xì)節(jié),我在之前的文章中有提及士聪,這里也一并推薦锦援,感謝大家的閱讀,如果稍有收獲剥悟,麻煩點個在看灵寺,你的支持是我創(chuàng)作的最大動力~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末区岗,一起剝皮案震驚了整個濱河市略板,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌慈缔,老刑警劉巖叮称,帶你破解...
    沈念sama閱讀 221,198評論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異藐鹤,居然都是意外死亡瓤檐,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評論 3 398
  • 文/潘曉璐 我一進(jìn)店門娱节,熙熙樓的掌柜王于貴愁眉苦臉地迎上來挠蛉,“玉大人,你說我怎么就攤上這事肄满∏垂牛” “怎么了绍移?”我有些...
    開封第一講書人閱讀 167,643評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長讥电。 經(jīng)常有香客問我蹂窖,道長,這世上最難降的妖魔是什么恩敌? 我笑而不...
    開封第一講書人閱讀 59,495評論 1 296
  • 正文 為了忘掉前任瞬测,我火速辦了婚禮,結(jié)果婚禮上纠炮,老公的妹妹穿的比我還像新娘月趟。我一直安慰自己,他們只是感情好恢口,可當(dāng)我...
    茶點故事閱讀 68,502評論 6 397
  • 文/花漫 我一把揭開白布孝宗。 她就那樣靜靜地躺著,像睡著了一般耕肩。 火紅的嫁衣襯著肌膚如雪因妇。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,156評論 1 308
  • 那天猿诸,我揣著相機與錄音婚被,去河邊找鬼。 笑死梳虽,一個胖子當(dāng)著我的面吹牛址芯,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播窜觉,決...
    沈念sama閱讀 40,743評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼谷炸,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了禀挫?” 一聲冷哼從身側(cè)響起旬陡,我...
    開封第一講書人閱讀 39,659評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎特咆,沒想到半個月后季惩,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,200評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡腻格,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,282評論 3 340
  • 正文 我和宋清朗相戀三年画拾,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片菜职。...
    茶點故事閱讀 40,424評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡青抛,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出酬核,到底是詐尸還是另有隱情蜜另,我是刑警寧澤适室,帶...
    沈念sama閱讀 36,107評論 5 349
  • 正文 年R本政府宣布,位于F島的核電站举瑰,受9級特大地震影響捣辆,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜此迅,卻給世界環(huán)境...
    茶點故事閱讀 41,789評論 3 333
  • 文/蒙蒙 一汽畴、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧耸序,春花似錦忍些、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至搅窿,卻和暖如春嘁酿,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背戈钢。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評論 1 271
  • 我被黑心中介騙來泰國打工痹仙, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留是尔,地道東北人殉了。 一個月前我還...
    沈念sama閱讀 48,798評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像拟枚,于是被迫代替她去往敵國和親薪铜。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,435評論 2 359

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