python面試學(xué)習(xí)路線-4.中間件

4.中間件

要求: 能熟練使用棵里、部署粒褒、調(diào)優(yōu)哗蜈、問題排查茁瘦、懂原理

1.緩存中間件: Redis/Memecache

1.redis有什么缺點

  • 緩存和數(shù)據(jù)庫雙寫一致性問題
  • 緩存雪崩問題 (所有key一起失效)
  • 緩存擊穿問題 (key失效一起訪問MySQL)
  • 緩存穿透問題 (Redis和MySQL都沒有數(shù)據(jù))
  • 緩存的并發(fā)競爭問題 (兩個鏈接同時對一個key的值操作)

2.如何應(yīng)對Redis 擊穿纽竣、穿透墓贿、雪崩 如何應(yīng)對并發(fā)競爭key

3.單線程的Redis為什么這么快?

  • 多路復(fù)用

  • Resp協(xié)議

  • 單線程

  • 內(nèi)存操作

為什么快:

4.Redis數(shù)據(jù)類型和使用場景

  • String:一般做一些復(fù)雜的計數(shù)功能的緩存;
  • Hash:存儲二維數(shù)據(jù)或?qū)ο螅?/li>
  • List:可實現(xiàn)隊列蜓氨,棧及有序的數(shù)據(jù)存儲聋袋;
  • Set:常用于黑名單,微信抽獎等功能穴吹,應(yīng)用場景多變幽勒;
  • SortedSet:做排行榜應(yīng)用,取TOPN操作港令;延時任務(wù)代嗤;做范圍查找

5.Redis過期策略和內(nèi)存淘汰機(jī)制

惰性刪除策略

6.Redis和數(shù)據(jù)庫雙寫一致性問題

最終一致性和強(qiáng)一致性

  1. 強(qiáng)一致性:在任何時刻所有的用戶或者進(jìn)程查詢到的都是最近一次成功更新的數(shù)據(jù)。強(qiáng)一致性是程度最高一致性要求缠借,也是最難實現(xiàn)的干毅。關(guān)系型數(shù)據(jù)庫更新操作就是這個案例。

  2. 最終一致性:和強(qiáng)一致性相對泼返,在某一時刻用戶或者進(jìn)程查詢到的數(shù)據(jù)可能都不同硝逢,但是最終成功更新的數(shù)據(jù)都會被所有用戶或者進(jìn)程查詢到。

強(qiáng)一致性 三種更新策略

理論上給緩存設(shè)置過期時間可以保證最終一致性的解決方案,但是在這種方案下,設(shè)置過期時間過長會導(dǎo)致錯誤數(shù)據(jù)過多,設(shè)置過期時間過短會導(dǎo)致影響并發(fā)性能問題

  • 1.先更新數(shù)據(jù)庫,在更新緩存
  • 2.先更新數(shù)據(jù)庫,在刪除緩存
  • 3.先刪除緩存,在更新數(shù)據(jù)庫

第1種,

  1. 請求A更新數(shù)據(jù)庫
  2. 請求B更新數(shù)據(jù)庫
  3. 請求B更新緩存
  4. 請求A更新緩存

或者:

  1. 請求A查詢緩存發(fā)現(xiàn)沒有,從數(shù)據(jù)庫拿到舊值
  2. 請求B更新數(shù)據(jù)庫, 更新緩存
  3. 請求A把拿到的舊值,更新緩存

因為網(wǎng)絡(luò)等原因,或者業(yè)務(wù)代碼執(zhí)行時間原因,會導(dǎo)致臟數(shù)據(jù).

MySQL集群,讀寫分離,查詢拿值比寫入更快,是最不建議的

第2種,

  1. 請求A更新數(shù)據(jù)庫,刪除緩存
  2. 請求B拿取數(shù)據(jù)庫舊值,更新緩存

一般因為讀寫分離,如果兩部同時發(fā)生,第二步會比第一步更快操作緩存,但是如果有意外發(fā)生,可以設(shè)置延遲刪除,但是如果刪除失敗呢, 也比較不可取

第3種,

  1. 請求A刪除緩存,更新數(shù)據(jù)庫
  2. 請求B發(fā)現(xiàn)緩存不存在,從數(shù)據(jù)庫拿取舊值,更新緩存

即使用了行鎖,請求A和請求B同時執(zhí)行,請求B先于A從庫里讀取數(shù)據(jù),也會把臟數(shù)據(jù)存到緩存,同時先刪除緩存如果數(shù)據(jù)庫更新失敗,緩存無法直接恢復(fù)的問題,不可取

延時雙刪策略

  1. 先刪除緩存
  2. 更新數(shù)據(jù)庫, 更新緩存
  3. 再休眠一段時間,再次刪除緩存

這么做,可以將一段時間的緩存可能存在的臟數(shù)據(jù),再次刪除

那么這個一段時間,具體該設(shè)置多久呢,即使根據(jù)業(yè)務(wù)設(shè)計幾百ms,在高并發(fā)的場景下也會具有一定的風(fēng)險性,并且頻繁的刪除從寫,也會影響性能

名為《Cache-Aside pattern》的緩存更新策略中指出

  • 失效:應(yīng)用程序先從cache取數(shù)據(jù),沒有得到渠鸽,則從數(shù)據(jù)庫中取數(shù)據(jù)叫乌,成功后,放到緩存中徽缚。
  • 命中:應(yīng)用程序從cache中取數(shù)據(jù)憨奸,取到后返回。
  • 更新:先把數(shù)據(jù)存到數(shù)據(jù)庫中凿试,成功后排宰,再讓緩存失效。

目前有一個比較完善的針對第2種解決方案的完善方案

image-20200419171411978.png

流程如下圖所示:
(1)更新數(shù)據(jù)庫數(shù)據(jù)
(2)數(shù)據(jù)庫會將操作信息寫入binlog日志當(dāng)中
(3)訂閱程序提取出所需要的數(shù)據(jù)以及key
(4)另起一段非業(yè)務(wù)代碼那婉,獲得該信息
(5)嘗試刪除緩存操作板甘,發(fā)現(xiàn)刪除失敗
(6)將這些信息發(fā)送至消息隊列
(7)重新從消息隊列中獲得該數(shù)據(jù),重試操作详炬。

訂閱binlog程序在mysql中有現(xiàn)成的中間件叫canal盐类,可以完成訂閱binlog日志的功能.

上述策略略微負(fù)責(zé),在時間充裕的時候可以實現(xiàn),那有么有特殊的場景和過度方法呢?

版本校驗策略

如果更新的數(shù)據(jù)是數(shù)字,同時數(shù)據(jù)的一致性和在一定延時的情況下準(zhǔn)確性要求不高,可以考慮

比如點贊,收藏,關(guān)注,瀏覽數(shù)等等,把數(shù)量當(dāng)成迭代版本號,數(shù)量越大時越是新的數(shù)據(jù),采用方案1,先更新數(shù)據(jù)庫在更新緩存,在更新緩存時,先進(jìn)行比較.如果數(shù)量小,就不更新.多數(shù)情況下,避免臟數(shù)據(jù)的產(chǎn)生.

最終一致性

如果對數(shù)據(jù)一致性和準(zhǔn)確性,有嚴(yán)格的一致的要求,

比如商品秒殺、搶購場景.可以直接只使用緩存作為唯一真實來源,等活動結(jié)束,背后批量去更新數(shù)據(jù)庫

異構(gòu)數(shù)據(jù)庫確實很難保持強(qiáng)一致性,利用補(bǔ)償機(jī)制和減少時間窗口的方法,以及設(shè)置過期時間兜底,達(dá)到最終的一致.

7.CAP原理與強(qiáng)一致性呛谜、弱一致性在跳、最終一致性

  • 一致性(Consistency)
  • 可用性(Availability)
  • 分區(qū)容忍性(Partition tolerance)

CAP原理指的是,這三個要素最多只能同時實現(xiàn)兩點隐岛,不可能三者兼顧硬毕。因此在進(jìn)行分布式架構(gòu)設(shè)計時,必須做出取舍礼仗。而對于分布式數(shù)據(jù)系統(tǒng)吐咳,分區(qū)容忍性是基本要求,否則就失去了價值元践。因此設(shè)計分布式數(shù)據(jù)系統(tǒng)韭脊,就是在一致性和可用性之間取一個平衡。對于大多數(shù)web應(yīng)用单旁,其實并不需要強(qiáng)一致性沪羔,因此犧牲一致性而換取高可用性,是目前多數(shù)分布式數(shù)據(jù)庫產(chǎn)品的方向

當(dāng)然象浑,犧牲一致性蔫饰,并不是完全不管數(shù)據(jù)的一致性,否則數(shù)據(jù)是混亂的愉豺,那么系統(tǒng)可用性再高分布式再好也沒有了價值篓吁。犧牲一致性,只是不再要求關(guān)系型數(shù)據(jù)庫中的強(qiáng)一致性蚪拦,而是只要系統(tǒng)能達(dá)到最終一致性即可杖剪,考慮到客戶體驗冻押,這個最終一致的時間窗口,要盡可能的對用戶透明盛嘿,也就是需要保障“用戶感知到的一致性”洛巢。通常是通過數(shù)據(jù)的多份異步復(fù)制來實現(xiàn)系統(tǒng)的高可用和數(shù)據(jù)的最終一致性的,“用戶感知到的一致性”的時間窗口則取決于數(shù)據(jù)復(fù)制到一致狀態(tài)的時間次兆。

從客戶端角度稿茉,多進(jìn)程并發(fā)訪問時,更新過的數(shù)據(jù)在不同進(jìn)程如何獲取的不同策略芥炭,決定了不同的一致性漓库。對于關(guān)系型數(shù)據(jù)庫,要求更新過的數(shù)據(jù)能被后續(xù)的訪問都能看到蚤认,這是強(qiáng)一致性米苹。如果能容忍后續(xù)的部分或者全部訪問不到糕伐,則是弱一致性砚殿。如果經(jīng)過一段時間后要求能訪問到更新后的數(shù)據(jù)俊抵,則是最終一致性

  • 因果一致性。如果進(jìn)程A通知進(jìn)程B它已更新了一個數(shù)據(jù)項骤素,那么進(jìn)程B的后續(xù)訪問將返回更新后的值,且一次寫入將保證取代前一次寫入酗捌。與進(jìn)程A無因果關(guān)系的進(jìn)程C的訪問遵守一般的最終一致性規(guī)則妈橄。
  • “讀己之所寫(read-your-writes)”一致性。當(dāng)進(jìn)程A自己更新一個數(shù)據(jù)項之后赞庶,它總是訪問到更新過的值训挡,絕不會看到舊值。這是因果一致性模型的一個特例歧强。
  • 會話(Session)一致性澜薄。這是上一個模型的實用版本,它把訪問存儲系統(tǒng)的進(jìn)程放到會話的上下文中摊册。只要會話還存在肤京,系統(tǒng)就保證“讀己之所寫”一致性。如果由于某些失敗情形令會話終止茅特,就要建立新的會話忘分,而且系統(tǒng)的保證不會延續(xù)到新的會話。
  • 單調(diào)(Monotonic)讀一致性白修。如果進(jìn)程已經(jīng)看到過數(shù)據(jù)對象的某個值妒峦,那么任何后續(xù)訪問都不會返回在那個值之前的值。
  • 單調(diào)寫一致性兵睛。系統(tǒng)保證來自同一個進(jìn)程的寫操作順序執(zhí)行舟山。要是系統(tǒng)不能保證這種程度的一致性绸狐,就非常難以編程了。

7.Redis布隆過濾器(Bloom Filter)

image-20200420173316564.png

先查詢緩存累盗,緩存不命中再查詢數(shù)據(jù)庫寒矿。 然后將查詢結(jié)果放在緩存中即使數(shù)據(jù)不存在,也需要創(chuàng)建一個緩存若债,用來防止穿庫符相。這里需要區(qū)分一下數(shù)據(jù)是否存在。 如果數(shù)據(jù)不存在蠢琳,緩存時間可以設(shè)置相對較短啊终,防止因為主從同步等問題,導(dǎo)致問題被放大傲须。

這個流程中存在薄弱的問題是蓝牲,當(dāng)用戶量太大時,我們會緩存大量數(shù)據(jù)空數(shù)據(jù)泰讽,并且一旦來一波冷用戶例衍,會造成雪崩效應(yīng)。 對于這種情況已卸,我們產(chǎn)生第二個版本流程:redis過濾冷用戶緩存流程

image-20200420173414101.png

引用鏈接:https://www.cnblogs.com/yscl/p/12003359.html

什么是布隆過濾器?

本質(zhì)上布隆過濾器( BloomFilter )是一種數(shù)據(jù)結(jié)構(gòu)佛玄,比較巧妙的概率型數(shù)據(jù)結(jié)構(gòu)(probabilistic data structure),特點是高效地插入和查詢累澡,可以用來告訴你 “某樣?xùn)|西一定不存在或者可能存在”梦抢。

相比于傳統(tǒng)的 Set、Map 等數(shù)據(jù)結(jié)構(gòu)愧哟,它更高效奥吩、占用空間更少,但是缺點是其返回的結(jié)果是概率性的蕊梧,而不是確切的霞赫。

布隆過濾器原理

布隆過濾器內(nèi)部維護(hù)一個bitArray(位數(shù)組), 開始所有數(shù)據(jù)全部置 0 望几。當(dāng)一個元素過來時绩脆,能過多個哈希函數(shù)(hash1,hash2,hash3....)計算不同的在哈希值,并通過哈希值找到對應(yīng)的bitArray下標(biāo)處橄抹,將里面的值 0 置為 1 靴迫。 需要說明的是,布隆過濾器有一個誤判率的概念楼誓,誤判率越低玉锌,則數(shù)組越長,所占空間越大疟羹。誤判率越高則數(shù)組越小主守,所占的空間越小禀倔。

下面以網(wǎng)址為例來進(jìn)行說明, 例如布隆過濾器的初始情況如下圖所示:


1534894-20191207205013684-856617678.jpg

現(xiàn)在我們需要往布隆過濾里中插入baidu這個url,經(jīng)過3個哈希函數(shù)的計算参淫,hash值分別為1救湖,4,7涎才,那么我們就需要對布隆過濾器的對應(yīng)的bit位置1鞋既, 就如圖下所示:

img

接下來,需要繼續(xù)往布隆過濾器中添加tencent這個url耍铜,然后它計算出來的hash值分別3邑闺,4,8棕兼,繼續(xù)往對應(yīng)的bit位置1陡舅。這里就需要注意一個點, 上面兩個url最后計算出來的hash值都有4伴挚,這個現(xiàn)象也是布隆不能確認(rèn)某個元素一定存在的原因靶衍,最后如下圖所示:

1534894-20191207205113779-698386268.jpg

布隆過濾器的查詢也很簡單,例如我們需要查找python章鲤,只需要計算出它的hash值摊灭, 如果該值為2咆贬,4败徊,7,那么因為對應(yīng)bit位上的數(shù)據(jù)有一個不為1掏缎, 那么一定可以斷言python不存在皱蹦,但是如果它計算的hash值是1,3眷蜈,7沪哺,那么就只能判斷出python可能存在,這個例子就可以看出來酌儒, 我們沒有存入python辜妓,但是由于其他key存儲的時候返回的hash值正好將python計算出來的hash值對應(yīng)的bit位占用了,這樣就不能準(zhǔn)確地判斷出python是否存在忌怎。

因此籍滴, 隨著添加的值越來越多, 被占的bit位越來越多榴啸, 這時候誤判的可能性就開始變高孽惰,如果布隆過濾器所有bit位都被置為1的話,那么所有key都有可能存在鸥印, 這時候布隆過濾器也就失去了過濾的功能勋功。至此坦报,選擇一個合適的過濾器長度就顯得非常重要。

從上面布隆過濾器的實現(xiàn)原理可以看出狂鞋,它不支持刪除片择, 一旦將某個key對應(yīng)的bit位置0,可能會導(dǎo)致同樣bit位的其他key的存在性判斷錯誤骚揍。

布隆過濾器的準(zhǔn)確性

布隆過濾器的核心思想有兩點:

  1. 多個hash构回,增大隨機(jī)性,減少hash碰撞的概率
  2. 擴(kuò)大數(shù)組范圍疏咐,使hash值均勻分布纤掸,進(jìn)一步減少hash碰撞的概率。

雖然布隆過濾器已經(jīng)盡可能的減小hash碰撞的概率了浑塞,但是借跪,并不能徹底消除,因此正如上面的小例子所舉的小例子的結(jié)果來看酌壕, 布隆過濾器只能告訴我們某樣?xùn)|西一定不存在以及它可能存在掏愁。

關(guān)于布隆過濾器的數(shù)組大小以及相應(yīng)的hash函數(shù)個數(shù)的選擇, 可以參考網(wǎng)上的其他博客或者是這個維基百科上對應(yīng)詞條上的結(jié)果:Probability of false positives .

1534894-20191207205508471-460587233.png

上圖的縱坐標(biāo)p是誤判率卵牍,橫坐標(biāo)n表示插入的元素個數(shù)果港,m表示布隆過濾器的bit長度,當(dāng)然上圖結(jié)果成立都假設(shè)hash函數(shù)的個數(shù)k滿足條件k = (m/n)ln2(忽略k是整數(shù))糊昙。

從上面的結(jié)果來看辛掠, 選擇合適后誤判率還是比較低的。

布隆過濾器的應(yīng)用

  1. 網(wǎng)頁爬蟲對URL的去重释牺,避免爬取相同的URL地址
  2. 反垃圾郵件萝衩,從數(shù)十億個垃圾郵件列表中判斷某郵箱是否垃圾郵箱(同理,垃圾短信)
  3. 緩存穿透没咙,將所有可能存在的數(shù)據(jù)緩存放到布隆過濾器中猩谊,當(dāng)黑客訪問不存在的緩存時迅速返回避免緩存及DB掛掉。
  4. 黑名單過濾祭刚,

布隆過濾器的使用

  1. Python版,不依賴Redis

安裝: pip3 install pybloom_live

from pybloom_live import ScalableBloomFilter

# mode=ScalableBloomFilter.SMALL_SET_GROWTH
sbf = ScalableBloomFilter(initial_capacity=100, error_rate=0.001, mode=ScalableBloomFilter.LARGE_SET_GROWTH)

url = "皮卡丘1"
url2 = "皮卡丘2"

sbf.add(url)

print(url in sbf)  # True
print(url2 in sbf)  # False

如果需要去重的數(shù)據(jù)量不是特別大,推薦用這種方式, BloomFilter(定容)和ScalableBloomFilter(可伸縮的),ScalableBloomFilter超過參數(shù)默認(rèn)值,也可以使用,推薦mode用默認(rèn)

  1. Redis中使用布隆過濾器

詳細(xì)的文檔可以參考官方文檔牌捷。

這個模塊不僅僅實現(xiàn)了布隆過濾器,還實現(xiàn)了 CuckooFilter(布谷鳥過濾器)涡驮,以及 TopK功能暗甥。CuckooFilter是在 BloomFilter的基礎(chǔ)上主要解決了BloomFilter不能刪除的缺點。 下面只說明了布隆過濾器

安裝

傳統(tǒng)的redis服務(wù)器安裝 RedisBloom 插件遮怜,詳情可以參考centos中安裝redis插件bloom-filter

127.0.0.1:6379> bf.reserve black_male 0.001 1000
OK

bf.reserve {key} {error_rate} {size} # 創(chuàng)建一個空的名為key的布隆過濾器淋袖,并設(shè)置一個期望的錯誤率和初始大小。{error_rate}過濾器的錯誤率在0-1之間

bf.add {key} {item} # 往過濾器中添加元素锯梁。如果key不存在即碗,過濾器會自動創(chuàng)建

bf.madd {key} {item} [item…] # 批量添加

bf.exists {key} {item} # 判斷過濾器中是否存在該元素焰情,不存在返回0,存在返回1

bf.mexists {key} {item} [item…] # 批量判斷存在

2.消息中間件: ActiveMQ/RabbitMQ/kafka/RocketMQ

3.負(fù)載均衡中間件: Nginx(LVS/CDN/DNS)

4.數(shù)據(jù)庫中間: mycat, 最好熟悉NewSQL

5.WEB服務(wù)器: Tomcat/jetty/Nginx, 傳統(tǒng)行業(yè): weblogic jboss websphere

6.搜索引擎: Lucene/solr/elasticsearch

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末剥懒,一起剝皮案震驚了整個濱河市内舟,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌初橘,老刑警劉巖验游,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異保檐,居然都是意外死亡耕蝉,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進(jìn)店門夜只,熙熙樓的掌柜王于貴愁眉苦臉地迎上來垒在,“玉大人,你說我怎么就攤上這事扔亥〕∏” “怎么了?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵旅挤,是天一觀的道長踢关。 經(jīng)常有香客問我,道長粘茄,這世上最難降的妖魔是什么签舞? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮驹闰,結(jié)果婚禮上瘪菌,老公的妹妹穿的比我還像新娘撒会。我一直安慰自己嘹朗,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布诵肛。 她就那樣靜靜地躺著屹培,像睡著了一般。 火紅的嫁衣襯著肌膚如雪怔檩。 梳的紋絲不亂的頭發(fā)上褪秀,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天,我揣著相機(jī)與錄音薛训,去河邊找鬼媒吗。 笑死,一個胖子當(dāng)著我的面吹牛乙埃,可吹牛的內(nèi)容都是我干的闸英。 我是一名探鬼主播锯岖,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼甫何!你這毒婦竟也來了出吹?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤辙喂,失蹤者是張志新(化名)和其女友劉穎捶牢,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體巍耗,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡秋麸,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了炬太。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片竹勉。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖娄琉,靈堂內(nèi)的尸體忽然破棺而出次乓,到底是詐尸還是另有隱情,我是刑警寧澤孽水,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布票腰,位于F島的核電站,受9級特大地震影響女气,放射性物質(zhì)發(fā)生泄漏杏慰。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一炼鞠、第九天 我趴在偏房一處隱蔽的房頂上張望缘滥。 院中可真熱鬧,春花似錦谒主、人聲如沸朝扼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽擎颖。三九已至,卻和暖如春观游,著一層夾襖步出監(jiān)牢的瞬間搂捧,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工懂缕, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留允跑,地道東北人。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像聋丝,于是被迫代替她去往敵國和親荤崇。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,877評論 2 345

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