Paxos算法與Zookeeper分析

Paxos算法與Zookeeper分析

1 Paxos算法

1.1?基本定義

算法中的參與者主要分為三個(gè)角色砸琅,同時(shí)每個(gè)參與者又可兼領(lǐng)多個(gè)角色:

⑴proposer提出提案迫摔,提案信息包括提案編號(hào)和提議的value;

⑵acceptor收到提案后可以接受(accept)提案;

⑶learner只能"學(xué)習(xí)"被批準(zhǔn)的提案;

算法保重一致性的基本語(yǔ)義:

⑴決議(value)只有在被proposers提出后才能被批準(zhǔn)(未經(jīng)批準(zhǔn)的決議稱為"提案(proposal)");

⑵在一次Paxos算法的執(zhí)行實(shí)例中,只批準(zhǔn)(chosen)一個(gè)value;

⑶learners只能獲得被批準(zhǔn)(chosen)的value;

有上面的三個(gè)語(yǔ)義可演化為四個(gè)約束:

⑴P1:一個(gè)acceptor必須接受(accept)第一次收到的提案;

⑵P2a:一旦一個(gè)具有value?v的提案被批準(zhǔn)(chosen)汤踏,那么之后任何acceptor再次接受(accept)的提案必須具有value?v;

⑶P2b:一旦一個(gè)具有value?v的提案被批準(zhǔn)(chosen)织鲸,那么以后任何proposer提出的提案必須具有value?v;

⑷P2c:如果一個(gè)編號(hào)為n的提案具有value?v,那么存在一個(gè)多數(shù)派溪胶,要么他們中所有人都沒(méi)有接受(accept)編號(hào)小于n的任何提案搂擦,要么他們已經(jīng)接受(accpet)的所有編號(hào)小于n的提案中編號(hào)最大的那個(gè)提案具有value?v;

1.2?基本算法(basic?paxos)

算法(決議的提出與批準(zhǔn))主要分為兩個(gè)階段:

1.prepare階段:

(1).當(dāng)Porposer希望提出方案V1,首先發(fā)出prepare請(qǐng)求至大多數(shù)Acceptor哗脖。Prepare請(qǐng)求內(nèi)容為序列號(hào);

(2).當(dāng)Acceptor接收到prepare請(qǐng)求時(shí)盾饮,檢查自身上次回復(fù)過(guò)的prepare請(qǐng)求

a).如果SN2>SN1,則忽略此請(qǐng)求,直接結(jié)束本次批準(zhǔn)過(guò)程;

b).否則檢查上次批準(zhǔn)的accept請(qǐng)求丘损,并且回復(fù)普办;如果之前沒(méi)有進(jìn)行過(guò)批準(zhǔn),則簡(jiǎn)單回復(fù);

2.?accept批準(zhǔn)階段:

(1a).經(jīng)過(guò)一段時(shí)間徘钥,收到一些Acceptor回復(fù)衔蹲,回復(fù)可分為以下幾種:

a).回復(fù)數(shù)量滿足多數(shù)派,并且所有的回復(fù)都是呈础,則Porposer發(fā)出accept請(qǐng)求舆驶,請(qǐng)求內(nèi)容為議案;

b).回復(fù)數(shù)量滿足多數(shù)派,但有的回復(fù)為:而钞,……則Porposer找到所有回復(fù)中超過(guò)半數(shù)的那個(gè)沙廉,假設(shè)為,則發(fā)出accept請(qǐng)求臼节,請(qǐng)求內(nèi)容為議案;

c).回復(fù)數(shù)量不滿足多數(shù)派撬陵,Proposer嘗試增加序列號(hào)為SN1+,轉(zhuǎn)1繼續(xù)執(zhí)行;

(1b).經(jīng)過(guò)一段時(shí)間网缝,收到一些Acceptor回復(fù)巨税,回復(fù)可分為以下幾種:

a).回復(fù)數(shù)量滿足多數(shù)派,則確認(rèn)V1被接受;

b).回復(fù)數(shù)量不滿足多數(shù)派粉臊,V1未被接受草添,Proposer增加序列號(hào)為SN1+,轉(zhuǎn)1繼續(xù)執(zhí)行;

(2).在不違背自己向其他proposer的承諾的前提下扼仲,acceptor收到accept請(qǐng)求后即接受并回復(fù)這個(gè)請(qǐng)求远寸。

1.3?算法優(yōu)化(fast?paxos)

Paxos算法在出現(xiàn)競(jìng)爭(zhēng)的情況下,其收斂速度很慢屠凶,甚至可能出現(xiàn)活鎖的情況而晒,例如當(dāng)有三個(gè)及三個(gè)以上的proposer在發(fā)送prepare請(qǐng)求后,很難有一個(gè)proposer收到半數(shù)以上的回復(fù)而不斷地執(zhí)行第一階段的協(xié)議阅畴。因此倡怎,為了避免競(jìng)爭(zhēng),加快收斂的速度贱枣,在算法中引入了一個(gè)Leader這個(gè)角色监署,在正常情況下同時(shí)應(yīng)該最多只能有一個(gè)參與者扮演Leader角色,而其它的參與者則扮演Acceptor的角色纽哥,同時(shí)所有的人又都扮演Learner的角色钠乏。

在這種優(yōu)化算法中,只有Leader可以提出議案春塌,從而避免了競(jìng)爭(zhēng)使得算法能夠快速地收斂而趨于一致晓避,此時(shí)的paxos算法在本質(zhì)上就退變?yōu)閮呻A段提交協(xié)議簇捍。但在異常情況下,系統(tǒng)可能會(huì)出現(xiàn)多Leader的情況俏拱,但這并不會(huì)破壞算法對(duì)一致性的保證暑塑,此時(shí)多個(gè)Leader都可以提出自己的提案,優(yōu)化的算法就退化成了原始的paxos算法锅必。

一個(gè)Leader的工作流程主要有分為三個(gè)階段:

(1).學(xué)習(xí)階段?向其它的參與者學(xué)習(xí)自己不知道的數(shù)據(jù)(決議);

(2).同步階段?讓絕大多數(shù)參與者保持?jǐn)?shù)據(jù)(決議)的一致性;

(3).服務(wù)階段?為客戶端服務(wù)事格,提議案;

1.3.1?學(xué)習(xí)階段

當(dāng)一個(gè)參與者成為了Leader之后,它應(yīng)該需要知道絕大多數(shù)的paxos實(shí)例搞隐,因此就會(huì)馬上啟動(dòng)一個(gè)主動(dòng)學(xué)習(xí)的過(guò)程驹愚。假設(shè)當(dāng)前的新Leader早就知道了1-134、138和139的paxos實(shí)例劣纲,那么它會(huì)執(zhí)行135-137和大于139的paxos實(shí)例的第一階段逢捺。如果只檢測(cè)到135和140的paxos實(shí)例有確定的值,那它最后就會(huì)知道1-135以及138-140的paxos實(shí)例癞季。

1.3.2?同步階段

此時(shí)的Leader已經(jīng)知道了1-135劫瞳、138-140的paxos實(shí)例,那么它就會(huì)重新執(zhí)行1-135的paxos實(shí)例余佛,以保證絕大多數(shù)參與者在1-135的paxos實(shí)例上是保持一致的。至于139-140的paxos實(shí)例窍荧,它并不馬上執(zhí)行138-140的paxos實(shí)例辉巡,而是等到在服務(wù)階段填充了136、137的paxos實(shí)例之后再執(zhí)行蕊退。這里之所以要填充間隔郊楣,是為了避免以后的Leader總是要學(xué)習(xí)這些間隔中的paxos實(shí)例,而這些paxos實(shí)例又沒(méi)有對(duì)應(yīng)的確定值瓤荔。

1.3.4?服務(wù)階段

Leader將用戶的請(qǐng)求轉(zhuǎn)化為對(duì)應(yīng)的paxos實(shí)例净蚤,當(dāng)然,它可以并發(fā)的執(zhí)行多個(gè)paxos實(shí)例输硝,當(dāng)這個(gè)Leader出現(xiàn)異常之后今瀑,就很有可能造成paxos實(shí)例出現(xiàn)間斷。

1.3.5?問(wèn)題

(1).Leader的選舉原則

(2).Acceptor如何感知當(dāng)前Leader的失敗点把,客戶如何知道當(dāng)前的Leader

(3).當(dāng)出現(xiàn)多Leader之后橘荠,如何kill掉多余的Leader

(4).如何動(dòng)態(tài)的擴(kuò)展Acceptor

2.Zookeeper

2.1?整體架構(gòu)

在Zookeeper集群中,主要分為三者角色郎逃,而每一個(gè)節(jié)點(diǎn)同時(shí)只能扮演一種角色哥童,這三種角色分別是:

(1).?Leader?接受所有Follower的提案請(qǐng)求并統(tǒng)一協(xié)調(diào)發(fā)起提案的投票,負(fù)責(zé)與所有的Follower進(jìn)行內(nèi)部的數(shù)據(jù)交換(同步);

(2).?Follower?直接為客戶端服務(wù)并參與提案的投票褒翰,同時(shí)與Leader進(jìn)行數(shù)據(jù)交換(同步);

(3).?Observer?直接為客戶端服務(wù)但并不參與提案的投票贮懈,同時(shí)也與Leader進(jìn)行數(shù)據(jù)交換(同步);

2.2?QuorumPeer的基本設(shè)計(jì)

Zookeeper對(duì)于每個(gè)節(jié)點(diǎn)QuorumPeer的設(shè)計(jì)相當(dāng)?shù)撵`活匀泊,QuorumPeer主要包括四個(gè)組件:客戶端請(qǐng)求接收器(ServerCnxnFactory)、數(shù)據(jù)引擎(ZKDatabase)朵你、選舉器(Election)各聘、核心功能組件(Leader/Follower/Observer)。其中:

(1).ServerCnxnFactory負(fù)責(zé)維護(hù)與客戶端的連接(接收客戶端的請(qǐng)求并發(fā)送相應(yīng)的響應(yīng));

(2).ZKDatabase負(fù)責(zé)存儲(chǔ)/加載/查找數(shù)據(jù)(基于目錄樹(shù)結(jié)構(gòu)的KV+操作日志+客戶端Session);

(3).Election負(fù)責(zé)選舉集群的一個(gè)Leader節(jié)點(diǎn);

(4).Leader/Follower/Observer一個(gè)QuorumPeer節(jié)點(diǎn)應(yīng)該完成的核心職責(zé);

2.3?QuorumPeer工作流程

2.3.1?Leader職責(zé)

Follower確認(rèn):等待所有的Follower連接注冊(cè)撬呢,若在規(guī)定的時(shí)間內(nèi)收到合法的Follower注冊(cè)數(shù)量伦吠,則確認(rèn)成功;否則魂拦,確認(rèn)失敗毛仪。

2.3.2?Follower職責(zé)

2.4?選舉算法

2.4.1LeaderElection選舉算法

選舉線程由當(dāng)前Server發(fā)起選舉的線程擔(dān)任,他主要的功能對(duì)投票結(jié)果進(jìn)行統(tǒng)計(jì)芯勘,并選出推薦的Server箱靴。選舉線程首先向所有Server發(fā)起一次詢問(wèn)(包括自己),被詢問(wèn)方荷愕,根據(jù)自己當(dāng)前的狀態(tài)作相應(yīng)的回復(fù)衡怀,選舉線程收到回復(fù)后,驗(yàn)證是否是自己發(fā)起的詢問(wèn)(驗(yàn)證xid是否一致)安疗,然后獲取對(duì)方的id(myid)抛杨,并存儲(chǔ)到當(dāng)前詢問(wèn)對(duì)象列表中,最后獲取對(duì)方提議?的

leader相關(guān)信息(id,zxid)荐类,并將這些?信息存儲(chǔ)到當(dāng)次選舉的投票記錄表中怖现,當(dāng)向所有Serve?r

都詢問(wèn)完以后,對(duì)統(tǒng)計(jì)結(jié)果進(jìn)行篩選并進(jìn)行統(tǒng)計(jì)玉罐,計(jì)算出當(dāng)次詢問(wèn)后獲勝的是哪一個(gè)Server屈嗤,并將當(dāng)前zxid最大的Server設(shè)置為當(dāng)前Server要推薦的Server(有可能是自己,也有可以是其它的Server吊输,根據(jù)投票結(jié)果而定饶号,但是每一個(gè)Server在第一次投票時(shí)都會(huì)投自己),如果此時(shí)獲勝的Server獲得n/2?+?1的Server票數(shù)季蚂,設(shè)置當(dāng)前推薦的leader為獲勝的Server茫船。根據(jù)獲勝的Server相關(guān)信息設(shè)置自己的狀態(tài)。每一個(gè)Server都重復(fù)以上流程直到選舉出Leader扭屁。

初始化選票(第一張選票):每個(gè)quorum節(jié)點(diǎn)一開(kāi)始都投給自己;

收集選票:使用UDP協(xié)議盡量收集所有quorum節(jié)點(diǎn)當(dāng)前的選票(單線程/同步方式)透硝,超時(shí)設(shè)置200ms;

統(tǒng)計(jì)選票:?1).每個(gè)quorum節(jié)點(diǎn)的票數(shù);

2).為自己產(chǎn)生一張新選票(zxid、myid均最大);

選舉成功:某一個(gè)quorum節(jié)點(diǎn)的票數(shù)超過(guò)半數(shù);

更新選票:在本輪選舉失敗的情況下疯搅,當(dāng)前quorum節(jié)點(diǎn)會(huì)從收集的選票中選取合適的選票(zxid濒生、myid均最大)作為自己下一輪選舉的投票;

異常問(wèn)題的處理

1).?選舉過(guò)程中,Server的加入

當(dāng)一個(gè)Server啟動(dòng)時(shí)它都會(huì)發(fā)起一次選舉幔欧,此時(shí)由選舉線程發(fā)起相關(guān)流程罪治,那么每個(gè)Serve?r都會(huì)獲得當(dāng)前zxi?d最大的哪個(gè)Serve?r是誰(shuí)丽声,如果當(dāng)次最大的Serve?r沒(méi)有獲得n/2+1個(gè)票數(shù),那么下一次投票時(shí)觉义,他將向zxid最大的Server投票雁社,重復(fù)以上流程,最后一定能選舉出一個(gè)Leader晒骇。

2).?選舉過(guò)程中霉撵,Server的退出

只要保證n/2+1個(gè)Server存活就沒(méi)有任何問(wèn)題,如果少于n/2+1個(gè)Server存活就沒(méi)辦法選出Leader洪囤。

3).?選舉過(guò)程中徒坡,Leader死亡

當(dāng)選舉出Leader以后,此時(shí)每個(gè)Server應(yīng)該是什么狀態(tài)(FLLOWING)都已經(jīng)確定瘤缩,此時(shí)由于Leader已經(jīng)死亡我們就不管它喇完,其它的Fllower按正常的流程繼續(xù)下去,當(dāng)完成這個(gè)流程以后剥啤,所有的Fllower都會(huì)向Leader發(fā)送Ping消息锦溪,如果無(wú)法ping通,就改變自己的狀為(FLLOWING?==>?LOOKING)府怯,發(fā)起新的一輪選舉刻诊。

4).?選舉完成以后,Leader死亡

處理過(guò)程同上牺丙。

5).?雙主問(wèn)題

Leader的選舉是保證只產(chǎn)生一個(gè)公認(rèn)的Leader的则涯,而且Follower重新選舉與舊Leader恢復(fù)并退出基本上是同時(shí)發(fā)生的,當(dāng)Follower無(wú)法ping同Leader是就認(rèn)為L(zhǎng)eader已經(jīng)出問(wèn)題開(kāi)始重新選舉赘被,Leader收到Follower的ping沒(méi)有達(dá)到半數(shù)以上則要退出Leader重新選舉是整。

2.4.2?FastLeaderElection選舉算法

FastLeaderElection是標(biāo)準(zhǔn)的fast?paxos的實(shí)現(xiàn)肖揣,它首先向所有Server提議自己要成為leader,當(dāng)其它Server收到提議以后,解決epoch和zxid的沖突算色,并接受對(duì)方的提議浇借,然后向?qū)Ψ桨l(fā)送接受提議完成的消息。

FastLeaderElection算法通過(guò)異步的通信方式來(lái)收集其它節(jié)點(diǎn)的選票彤断,同時(shí)在分析選票時(shí)又根據(jù)投票者的當(dāng)前狀態(tài)來(lái)作不同的處理野舶,以加快Leader的選舉進(jìn)程。

每個(gè)Server都一個(gè)接收線程池和一個(gè)發(fā)送線程池,在沒(méi)有發(fā)起選舉時(shí)宰衙,這兩個(gè)線程池處于阻塞狀態(tài)平道,直到有消息到來(lái)時(shí)才解除阻塞并處理消息,同時(shí)每個(gè)Serve?r都有一個(gè)選舉線程(可以發(fā)起選舉的線程擔(dān)任)供炼。

1).?主動(dòng)發(fā)起選舉端(選舉線程)的處理

首先自己的logicalclock加1一屋,然后生成notification消息窘疮,并將消息放入發(fā)送隊(duì)列中,?系統(tǒng)中配置有幾個(gè)Server就生成幾條消息冀墨,保證每個(gè)Server都能收到此消息闸衫,如果當(dāng)前Server的狀態(tài)是LOOKING就一直循環(huán)檢查接收隊(duì)列是否有消息,如果有消息诽嘉,根據(jù)消息中對(duì)方的狀態(tài)進(jìn)行相應(yīng)的處理蔚出。

2).主動(dòng)發(fā)送消息端(發(fā)送線程池)的處理

將要發(fā)送的消息由Notification消息轉(zhuǎn)換成ToSend消息,然后發(fā)送對(duì)方虫腋,并等待對(duì)方的回復(fù)骄酗。

3).?被動(dòng)接收消息端(接收線程池)的處理

將收到的消息轉(zhuǎn)換成Notification消息放入接收隊(duì)列中,如果對(duì)方Server的epoch小于logicalclock則向其發(fā)送一個(gè)消息(讓其更新epoch)岔乔;如果對(duì)方Server處于Looking狀態(tài)酥筝,自己則處于Following或Leading狀態(tài),則也發(fā)送一個(gè)消息(當(dāng)前Leader已產(chǎn)生雏门,讓其盡快收斂)嘿歌。

2.4.3?AuthFastLeaderElection選舉算法

AuthFastLeaderElection算法同F(xiàn)astLeaderElection算法基本一致,只是在消息中加入了認(rèn)證信息茁影,該算法在最新的Zookeeper中也建議棄用宙帝。

2.5?Zookeeper的API

名稱

同步

異步

watch

權(quán)限認(rèn)證

create

delete

exist

getData

setData

getACL

setACL

getChildren

sync

multi

createSession

closeSession

2.6?Zookeeper中的請(qǐng)求處理流程

2.6.1?Follower節(jié)點(diǎn)處理用戶的讀寫(xiě)請(qǐng)求

2.6.2?Leader節(jié)點(diǎn)處理寫(xiě)請(qǐng)求

值得注意的是, Follower/Leader上的讀操作時(shí)并行的募闲,讀寫(xiě)操作是串行的步脓,當(dāng)CommitRequestProcessor處理一個(gè)寫(xiě)請(qǐng)求時(shí),會(huì)阻塞之后所有的讀寫(xiě)請(qǐng)求浩螺。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末靴患,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子要出,更是在濱河造成了極大的恐慌鸳君,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,718評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件患蹂,死亡現(xiàn)場(chǎng)離奇詭異或颊,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)传于,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門囱挑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人沼溜,你說(shuō)我怎么就攤上這事平挑。” “怎么了系草?”我有些...
    開(kāi)封第一講書(shū)人閱讀 158,207評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵通熄,是天一觀的道長(zhǎng)否淤。 經(jīng)常有香客問(wèn)我,道長(zhǎng)棠隐,這世上最難降的妖魔是什么石抡? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,755評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮助泽,結(jié)果婚禮上啰扛,老公的妹妹穿的比我還像新娘。我一直安慰自己嗡贺,他們只是感情好隐解,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,862評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著诫睬,像睡著了一般煞茫。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上摄凡,一...
    開(kāi)封第一講書(shū)人閱讀 50,050評(píng)論 1 291
  • 那天续徽,我揣著相機(jī)與錄音,去河邊找鬼亲澡。 笑死钦扭,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的床绪。 我是一名探鬼主播客情,決...
    沈念sama閱讀 39,136評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼癞己!你這毒婦竟也來(lái)了膀斋?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,882評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤痹雅,失蹤者是張志新(化名)和其女友劉穎仰担,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體练慕,經(jīng)...
    沈念sama閱讀 44,330評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡惰匙,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,651評(píng)論 2 327
  • 正文 我和宋清朗相戀三年技掏,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了铃将。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,789評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡哑梳,死狀恐怖劲阎,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情鸠真,我是刑警寧澤悯仙,帶...
    沈念sama閱讀 34,477評(píng)論 4 333
  • 正文 年R本政府宣布龄毡,位于F島的核電站,受9級(jí)特大地震影響锡垄,放射性物質(zhì)發(fā)生泄漏沦零。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,135評(píng)論 3 317
  • 文/蒙蒙 一货岭、第九天 我趴在偏房一處隱蔽的房頂上張望路操。 院中可真熱鬧,春花似錦千贯、人聲如沸屯仗。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,864評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)魁袜。三九已至,卻和暖如春敦第,著一層夾襖步出監(jiān)牢的瞬間峰弹,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,099評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工芜果, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留垮卓,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,598評(píng)論 2 362
  • 正文 我出身青樓师幕,卻偏偏與公主長(zhǎng)得像粟按,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子霹粥,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,697評(píng)論 2 351

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